diff --git a/plugins/auth-backend/src/lib/EnvironmentHandler.ts b/plugins/auth-backend/src/lib/EnvironmentHandler.ts index 6c16b3a712..76c1f40b73 100644 --- a/plugins/auth-backend/src/lib/EnvironmentHandler.ts +++ b/plugins/auth-backend/src/lib/EnvironmentHandler.ts @@ -16,49 +16,56 @@ import express from 'express'; import { AuthProviderRouteHandlers } from '../providers/types'; -import { NotFoundError } from '@backstage/backend-common'; export type EnvironmentHandlers = { [key: string]: AuthProviderRouteHandlers; }; export class EnvironmentHandler implements AuthProviderRouteHandlers { - constructor(private readonly providers: EnvironmentHandlers) {} + constructor( + private readonly providerId: string, + private readonly providers: EnvironmentHandlers, + ) {} - private getProviderForEnv(req: express.Request): AuthProviderRouteHandlers { + private getProviderForEnv( + req: express.Request, + res: express.Response, + ): AuthProviderRouteHandlers | undefined { const env = req.query.env?.toString(); - if (!this.providers.hasOwnProperty(env)) { - throw new NotFoundError( - `No environment for ${env} found in this provider`, - ); + + if (this.providers.hasOwnProperty(env)) { + return this.providers[env]; } - return this.providers[env]; + + res.status(404).send( + `Missing configuration. +
+
+For this flow to work you need to supply a valid configuration for the "${env}" environment of the "${this.providerId}" provider.`, + ); + return undefined; } async start(req: express.Request, res: express.Response): Promise { - const provider = this.getProviderForEnv(req); - await provider.start(req, res); + const provider = this.getProviderForEnv(req, res); + await provider?.start(req, res); } async frameHandler( req: express.Request, res: express.Response, ): Promise { - const provider = this.getProviderForEnv(req); - await provider.frameHandler(req, res); + const provider = this.getProviderForEnv(req, res); + await provider?.frameHandler(req, res); } async refresh(req: express.Request, res: express.Response): Promise { - const provider = this.getProviderForEnv(req); - if (provider.refresh) { - await provider.refresh(req, res); - } + const provider = this.getProviderForEnv(req, res); + await provider?.refresh?.(req, res); } async logout(req: express.Request, res: express.Response): Promise { - const provider = this.getProviderForEnv(req); - if (provider.logout) { - await provider.logout(req, res); - } + const provider = this.getProviderForEnv(req, res); + await provider?.logout?.(req, res); } } diff --git a/plugins/auth-backend/src/providers/github/provider.ts b/plugins/auth-backend/src/providers/github/provider.ts index ed06c5786e..bd7d4aa712 100644 --- a/plugins/auth-backend/src/providers/github/provider.ts +++ b/plugins/auth-backend/src/providers/github/provider.ts @@ -135,16 +135,16 @@ export function createGithubProvider( logger: Logger, tokenIssuer: TokenIssuer, ) { + const providerId = 'github'; const envProviders: EnvironmentHandlers = {}; for (const [env, envConfig] of Object.entries(providerConfig)) { const config = (envConfig as unknown) as OAuthProviderConfig; const { secure, appOrigin } = config; - const callbackURLParam = `?env=${env}`; const opts = { clientID: config.clientId, clientSecret: config.clientSecret, - callbackURL: `${baseUrl}/github/handler/frame${callbackURLParam}`, + callbackURL: `${baseUrl}/${providerId}/handler/frame?env=${env}`, }; if (!opts.clientID || !opts.clientSecret) { @@ -163,12 +163,12 @@ export function createGithubProvider( envProviders[env] = new OAuthProvider(new GithubAuthProvider(opts), { disableRefresh: true, persistScopes: true, - providerId: 'github', + providerId, secure, baseUrl, appOrigin, tokenIssuer, }); } - return new EnvironmentHandler(envProviders); + return new EnvironmentHandler(providerId, envProviders); } diff --git a/plugins/auth-backend/src/providers/gitlab/provider.ts b/plugins/auth-backend/src/providers/gitlab/provider.ts index 950c414750..af1d57fcce 100644 --- a/plugins/auth-backend/src/providers/gitlab/provider.ts +++ b/plugins/auth-backend/src/providers/gitlab/provider.ts @@ -142,6 +142,7 @@ export function createGitlabProvider( logger: Logger, tokenIssuer: TokenIssuer, ) { + const providerId = 'gitlab'; const envProviders: EnvironmentHandlers = {}; for (const [env, envConfig] of Object.entries(providerConfig)) { @@ -152,12 +153,10 @@ export function createGitlabProvider( clientSecret, audience, } = (envConfig as unknown) as OAuthProviderConfig; - const callbackURLParam = `?env=${env}`; - const opts = { clientID: clientId, clientSecret: clientSecret, - callbackURL: `${baseUrl}/gitlab/handler/frame${callbackURLParam}`, + callbackURL: `${baseUrl}/${providerId}/handler/frame?env=${env}`, baseURL: audience, }; @@ -176,12 +175,12 @@ export function createGitlabProvider( envProviders[env] = new OAuthProvider(new GitlabAuthProvider(opts), { disableRefresh: true, - providerId: 'gitlab', + providerId, secure, baseUrl, appOrigin, tokenIssuer, }); } - return new EnvironmentHandler(envProviders); + return new EnvironmentHandler(providerId, envProviders); } diff --git a/plugins/auth-backend/src/providers/google/provider.ts b/plugins/auth-backend/src/providers/google/provider.ts index 04705b66c1..3023c09886 100644 --- a/plugins/auth-backend/src/providers/google/provider.ts +++ b/plugins/auth-backend/src/providers/google/provider.ts @@ -154,16 +154,16 @@ export function createGoogleProvider( logger: Logger, tokenIssuer: TokenIssuer, ) { + const providerId = 'google'; const envProviders: EnvironmentHandlers = {}; for (const [env, envConfig] of Object.entries(providerConfig)) { const config = (envConfig as unknown) as OAuthProviderConfig; const { secure, appOrigin } = config; - const callbackURLParam = `?env=${env}`; const opts = { clientID: config.clientId, clientSecret: config.clientSecret, - callbackURL: `${baseUrl}/google/handler/frame${callbackURLParam}`, + callbackURL: `${baseUrl}/${providerId}/handler/frame?env=${env}`, }; if (!opts.clientID || !opts.clientSecret) { @@ -181,12 +181,12 @@ export function createGoogleProvider( envProviders[env] = new OAuthProvider(new GoogleAuthProvider(opts), { disableRefresh: false, - providerId: 'google', + providerId, secure, baseUrl, appOrigin, tokenIssuer, }); } - return new EnvironmentHandler(envProviders); + return new EnvironmentHandler(providerId, envProviders); } diff --git a/plugins/auth-backend/src/providers/oauth2/provider.ts b/plugins/auth-backend/src/providers/oauth2/provider.ts index 1b5ee6d17e..60b6bc605b 100644 --- a/plugins/auth-backend/src/providers/oauth2/provider.ts +++ b/plugins/auth-backend/src/providers/oauth2/provider.ts @@ -152,16 +152,16 @@ export function createOAuth2Provider( logger: Logger, tokenIssuer: TokenIssuer, ) { + const providerId = 'oauth2'; const envProviders: EnvironmentHandlers = {}; for (const [env, envConfig] of Object.entries(providerConfig)) { const config = (envConfig as unknown) as GenericOAuth2ProviderConfig; const { secure, appOrigin } = config; - const callbackURLParam = `?env=${env}`; const opts = { clientID: config.clientId, clientSecret: config.clientSecret, - callbackURL: `${baseUrl}/oauth2/handler/frame${callbackURLParam}`, + callbackURL: `${baseUrl}/${providerId}/handler/frame?env=${env}`, authorizationURL: config.authorizationURL, tokenURL: config.tokenURL, }; @@ -186,7 +186,7 @@ export function createOAuth2Provider( envProviders[env] = new OAuthProvider(new OAuth2AuthProvider(opts), { disableRefresh: false, - providerId: 'oauth2', + providerId, secure, baseUrl, appOrigin, @@ -194,5 +194,5 @@ export function createOAuth2Provider( }); } - return new EnvironmentHandler(envProviders); + return new EnvironmentHandler(providerId, envProviders); } diff --git a/plugins/auth-backend/src/providers/okta/provider.ts b/plugins/auth-backend/src/providers/okta/provider.ts index e73843bb0c..01d7684e97 100644 --- a/plugins/auth-backend/src/providers/okta/provider.ts +++ b/plugins/auth-backend/src/providers/okta/provider.ts @@ -47,17 +47,16 @@ type PrivateInfo = { }; export class OktaAuthProvider implements OAuthProviderHandlers { - private readonly _strategy: any; - /** + /** * Due to passport-okta-oauth forcing options.state = true, * passport-oauth2 requires express-session to be installed * so that the 'state' parameter of the oauth2 flow can be stored. * This implementation of StateStore matches the NullStore found within * passport-oauth2, which is the StateStore implementation used when options.state = false, * allowing us to avoid using express-session in order to integrate with Okta. - */ + */ private _store: StateStore = { store(_req: express.Request, cb: any) { cb(null, null); @@ -65,25 +64,27 @@ export class OktaAuthProvider implements OAuthProviderHandlers { verify(_req: express.Request, _state: string, cb: any) { cb(null, true); }, - } + }; constructor(options: OAuthProviderOptions) { - this._strategy = new OktaStrategy({ - passReqToCallback: false as true, - ...options, - store: this._store, - response_type: 'code', - }, ( - accessToken: any, - refreshToken: any, - params: any, - rawProfile: passport.Profile, - done: PassportDoneCallback, - ) => { - const profile = makeProfileInfo(rawProfile, params.id_token); + this._strategy = new OktaStrategy( + { + passReqToCallback: false as true, + ...options, + store: this._store, + response_type: 'code', + }, + ( + accessToken: any, + refreshToken: any, + params: any, + rawProfile: passport.Profile, + done: PassportDoneCallback, + ) => { + const profile = makeProfileInfo(rawProfile, params.id_token); - done( - undefined, + done( + undefined, { providerInfo: { idToken: params.id_token, @@ -96,13 +97,14 @@ export class OktaAuthProvider implements OAuthProviderHandlers { { refreshToken, }, - ) - }); + ); + }, + ); } async start( req: express.Request, - options: Record + options: Record, ): Promise { const providerOptions = { ...options, @@ -113,7 +115,7 @@ export class OktaAuthProvider implements OAuthProviderHandlers { } async handler( - req: express.Request + req: express.Request, ): Promise<{ response: OAuthResponse; refreshToken: string }> { const { response, privateInfo } = await executeFrameHandlerStrategy< OAuthResponse, @@ -148,7 +150,6 @@ export class OktaAuthProvider implements OAuthProviderHandlers { }, profile, }); - } private async populateIdentity( @@ -173,17 +174,17 @@ export function createOktaProvider( logger: Logger, tokenIssuer: TokenIssuer, ) { + const providerId = 'okta'; const envProviders: EnvironmentHandlers = {}; for (const [env, envConfig] of Object.entries(providerConfig)) { const config = (envConfig as unknown) as OAuthProviderConfig; const { secure, appOrigin } = config; - const callbackURLParam = `?env=${env}`; const opts = { audience: config.audience, clientID: config.clientId, clientSecret: config.clientSecret, - callbackURL: `${baseUrl}/okta/handler/frame${callbackURLParam}`, + callbackURL: `${baseUrl}/${providerId}/handler/frame?env=${env}`, }; if (!opts.clientID || !opts.clientSecret || !opts.audience) { @@ -201,7 +202,7 @@ export function createOktaProvider( envProviders[env] = new OAuthProvider(new OktaAuthProvider(opts), { disableRefresh: false, - providerId: 'okta', + providerId, secure, baseUrl, appOrigin, @@ -209,5 +210,5 @@ export function createOktaProvider( }); } - return new EnvironmentHandler(envProviders); + return new EnvironmentHandler(providerId, envProviders); } diff --git a/plugins/auth-backend/src/providers/saml/provider.ts b/plugins/auth-backend/src/providers/saml/provider.ts index 68b84c7e33..40c80de4e4 100644 --- a/plugins/auth-backend/src/providers/saml/provider.ts +++ b/plugins/auth-backend/src/providers/saml/provider.ts @@ -147,5 +147,5 @@ export function createSamlProvider( envProviders[env] = new SamlAuthProvider(opts); } - return new EnvironmentHandler(envProviders); + return new EnvironmentHandler('saml', envProviders); }