diff --git a/plugins/auth-node/src/index.ts b/plugins/auth-node/src/index.ts index a376c87338..e7b0b628b8 100644 --- a/plugins/auth-node/src/index.ts +++ b/plugins/auth-node/src/index.ts @@ -25,6 +25,7 @@ export * from './flow'; export * from './identity'; export * from './oauth'; export * from './passport'; +export * from './proxy'; export * from './sign-in'; export type { AuthProviderConfig, diff --git a/plugins/auth-node/src/proxy/createProxyAuthProviderFactory.ts b/plugins/auth-node/src/proxy/createProxyAuthProviderFactory.ts new file mode 100644 index 0000000000..a50225e911 --- /dev/null +++ b/plugins/auth-node/src/proxy/createProxyAuthProviderFactory.ts @@ -0,0 +1,60 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + readDeclarativeSignInResolver, + SignInResolverFactory, +} from '../sign-in'; +import { + AuthProviderFactory, + ProfileTransform, + SignInResolver, +} from '../types'; +import { createProxyAuthRouteHandlers } from './createProxyRouteHandlers'; +import { ProxyAuthenticator } from './types'; + +export function createProxyAuthProviderFactory(options: { + authenticator: ProxyAuthenticator; + profileTransform?: ProfileTransform; + signInResolver?: SignInResolver; + signInResolverFactories?: Record< + string, + SignInResolverFactory + >; +}): AuthProviderFactory { + return ctx => { + const signInResolver = + options.signInResolver ?? + readDeclarativeSignInResolver({ + config: ctx.config, + signInResolverFactories: options.signInResolverFactories ?? {}, + }); + + if (!signInResolver) { + throw new Error( + `No sign-in resolver configured for proxy auth provider '${ctx.providerId}'`, + ); + } + + return createProxyAuthRouteHandlers({ + signInResolver, + config: ctx.config, + authenticator: options.authenticator, + resolverContext: ctx.resolverContext, + profileTransform: options.profileTransform, + }); + }; +} diff --git a/plugins/auth-node/src/proxy/createProxyRouteHandlers.ts b/plugins/auth-node/src/proxy/createProxyRouteHandlers.ts new file mode 100644 index 0000000000..10ad62d2bf --- /dev/null +++ b/plugins/auth-node/src/proxy/createProxyRouteHandlers.ts @@ -0,0 +1,79 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Request, Response } from 'express'; +import { Config } from '@backstage/config'; +import { + AuthProviderRouteHandlers, + AuthResolverContext, + ClientAuthResponse, + ProfileTransform, + SignInResolver, +} from '../types'; +import { ProxyAuthenticator } from './types'; +import { prepareBackstageIdentityResponse } from '../identity'; + +/** @public */ +export interface ProxyAuthRouteHandlersOptions { + authenticator: ProxyAuthenticator; + config: Config; + resolverContext: AuthResolverContext; + signInResolver: SignInResolver; + profileTransform?: ProfileTransform; +} + +/** @public */ +export function createProxyAuthRouteHandlers( + options: ProxyAuthRouteHandlersOptions, +): AuthProviderRouteHandlers { + const { authenticator, config, resolverContext, signInResolver } = options; + + const profileTransform = + options.profileTransform ?? authenticator.defaultProfileTransform; + const authenticatorCtx = authenticator.initialize({ config }); + + return { + async start(): Promise { + throw new Error('Not implemented'); + }, + + async frameHandler(): Promise { + throw new Error('Not implemented'); + }, + + async refresh(this: never, req: Request, res: Response): Promise { + const { result } = await authenticator.authenticate( + { req }, + authenticatorCtx, + ); + + const { profile } = await profileTransform(result, resolverContext); + + const identity = await signInResolver( + { profile, result }, + resolverContext, + ); + + const response: ClientAuthResponse<{}> = { + profile, + providerInfo: {}, + backstageIdentity: prepareBackstageIdentityResponse(identity), + }; + + res.status(200).json(response); + }, + }; +} diff --git a/plugins/auth-node/src/proxy/index.ts b/plugins/auth-node/src/proxy/index.ts new file mode 100644 index 0000000000..907f8829e5 --- /dev/null +++ b/plugins/auth-node/src/proxy/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { createProxyAuthenticator, type ProxyAuthenticator } from './types'; +export { createProxyAuthProviderFactory } from './createProxyAuthProviderFactory'; +export { + createProxyAuthRouteHandlers, + type ProxyAuthRouteHandlersOptions, +} from './createProxyRouteHandlers'; diff --git a/plugins/auth-node/src/proxy/types.ts b/plugins/auth-node/src/proxy/types.ts new file mode 100644 index 0000000000..a7e0da2d4c --- /dev/null +++ b/plugins/auth-node/src/proxy/types.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config } from '@backstage/config'; +import { Request } from 'express'; +import { ProfileTransform } from '../types'; + +export interface ProxyAuthenticator { + defaultProfileTransform: ProfileTransform; + initialize(ctx: { config: Config }): Promise; + authenticate( + options: { req: Request }, + ctx: TContext, + ): Promise<{ result: TResult }>; +} + +export function createProxyAuthenticator( + authenticator: ProxyAuthenticator, +): ProxyAuthenticator { + return authenticator; +}