diff --git a/plugins/auth-backend/src/lib/EnvironmentHandler.ts b/plugins/auth-backend/src/lib/EnvironmentHandler.ts index 3eab4e793c..c9a182fc05 100644 --- a/plugins/auth-backend/src/lib/EnvironmentHandler.ts +++ b/plugins/auth-backend/src/lib/EnvironmentHandler.ts @@ -25,13 +25,28 @@ export class EnvironmentHandler implements AuthProviderRouteHandlers { constructor( private readonly providerId: string, private readonly providers: EnvironmentHandlers, + private readonly envIdentifier: (req: express.Request) => string, ) {} + /* Return the value of `env` key, if it is exists, encoded within + the `state` parameter in the request + */ + private getEnv(stateParams: Array): string { + const envParams = stateParams.filter( + param => param.split('=')[0] === 'env', + ); + + if (envParams.length > 0) { + return envParams[0].split('=')[1]; + } + return ''; + } + private getProviderForEnv( req: express.Request, res: express.Response, ): AuthProviderRouteHandlers | undefined { - const env = req.query.env?.toString(); + const env: string = this.envIdentifier(req); if (env && this.providers.hasOwnProperty(env)) { return this.providers[env]; diff --git a/plugins/auth-backend/src/lib/OAuthProvider.ts b/plugins/auth-backend/src/lib/OAuthProvider.ts index 7a4a9e2537..ca8872cd95 100644 --- a/plugins/auth-backend/src/lib/OAuthProvider.ts +++ b/plugins/auth-backend/src/lib/OAuthProvider.ts @@ -39,6 +39,21 @@ export type Options = { tokenIssuer: TokenIssuer; }; +/* Return the value of `env` key, if it is exists, encoded within + the `state` parameter in the request + */ +const getEnv = (stateParams: Array): string => { + const envParams = stateParams.filter(param => param.split('=')[0] === 'env'); + + if (envParams.length > 0) { + return envParams[0].split('=')[1]; + } + return ''; +}; + +const readState = (stateString: string): Array => { + return decodeURIComponent(stateString).split('&'); +}; export const verifyNonce = (req: express.Request, providerId: string) => { const cookieNonce = req.cookies[`${providerId}-nonce`]; const stateNonce = req.query.state; @@ -103,6 +118,7 @@ export class OAuthProvider implements AuthProviderRouteHandlers { async start(req: express.Request, res: express.Response): Promise { // retrieve scopes from request const scope = req.query.scope?.toString() ?? ''; + const env = req.query.env?.toString() ?? ''; if (this.options.persistScopes) { this.setScopesCookie(res, scope); @@ -112,9 +128,21 @@ export class OAuthProvider implements AuthProviderRouteHandlers { // set a nonce cookie before redirecting to oauth provider this.setNonceCookie(res, nonce); + // const stateObject: {nonce: string, + // env: string} + + const stateObject = { nonce: nonce, env: env }; + + const state = Object.keys(stateObject) + .map( + key => + `${encodeURIComponent(key)}=${encodeURIComponent(stateObject[key])}`, + ) + .join('&'); + const queryParameters = { scope, - state: nonce, + state: state, }; const { url, status } = await this.providerHandlers.start( @@ -224,6 +252,19 @@ export class OAuthProvider implements AuthProviderRouteHandlers { } } + identifyEnv(req: express.Request): string { + const reqEnv = req.query.env?.toString(); + if (reqEnv) { + return reqEnv; + } + const stateParam = req.query.state?.toString(); + if (!stateParam) { + return ''; + } + const env = getEnv(readState(stateParam)); + return env; + } + /** * If the response from the OAuth provider includes a Backstage identity, we * make sure it's populated with all the information we can derive from the user ID. diff --git a/plugins/auth-backend/src/providers/factories.ts b/plugins/auth-backend/src/providers/factories.ts index dcce98a986..f4144ddc6c 100644 --- a/plugins/auth-backend/src/providers/factories.ts +++ b/plugins/auth-backend/src/providers/factories.ts @@ -15,6 +15,7 @@ */ import Router from 'express-promise-router'; +import express from 'express'; import { Logger } from 'winston'; import { TokenIssuer } from '../identity'; import { createGithubProvider } from './github'; @@ -54,16 +55,24 @@ export const createAuthProviderRouter = ( const router = Router(); const envs = providerConfig.keys(); const envProviders: EnvironmentHandlers = {}; + let envIdentifier: (req: express.Request) => string; for (const env of envs) { const envConfig = providerConfig.getConfig(env); const provider = factory(globalConfig, env, envConfig, logger, issuer); if (provider) { envProviders[env] = provider; + if (envIdentifier === undefined) { + envIdentifier = provider.identifyEnv; + } } } - const handler = new EnvironmentHandler(providerId, envProviders); + const handler = new EnvironmentHandler( + providerId, + envProviders, + envIdentifier, + ); router.get('/start', handler.start.bind(handler)); router.get('/handler/frame', handler.frameHandler.bind(handler)); diff --git a/plugins/auth-backend/src/providers/oauth2/provider.ts b/plugins/auth-backend/src/providers/oauth2/provider.ts index 967fd011f0..c03f7c4371 100644 --- a/plugins/auth-backend/src/providers/oauth2/provider.ts +++ b/plugins/auth-backend/src/providers/oauth2/provider.ts @@ -143,7 +143,7 @@ export class OAuth2AuthProvider implements OAuthProviderHandlers { export function createOAuth2Provider( { baseUrl }: AuthProviderConfig, - env: string, + _: string, envConfig: Config, logger: Logger, tokenIssuer: TokenIssuer, @@ -153,7 +153,7 @@ export function createOAuth2Provider( const appOrigin = envConfig.getString('appOrigin'); const clientID = envConfig.getString('clientId'); const clientSecret = envConfig.getString('clientSecret'); - const callbackURL = `${baseUrl}/${providerId}/handler/frame?env=${env}`; + const callbackURL = `${baseUrl}/${providerId}/handler/frame`; const authorizationURL = envConfig.getString('authorizationURL'); const tokenURL = envConfig.getString('tokenURL'); diff --git a/plugins/auth-backend/src/providers/saml/provider.ts b/plugins/auth-backend/src/providers/saml/provider.ts index 3ddfba4bb0..605a46ece4 100644 --- a/plugins/auth-backend/src/providers/saml/provider.ts +++ b/plugins/auth-backend/src/providers/saml/provider.ts @@ -106,6 +106,10 @@ export class SamlAuthProvider implements AuthProviderRouteHandlers { async logout(_req: express.Request, res: express.Response): Promise { res.send('noop'); } + + identifyEnv(_req: express.Request): string { + return ''; + } } type SAMLProviderOptions = { diff --git a/plugins/auth-backend/src/providers/types.ts b/plugins/auth-backend/src/providers/types.ts index 93d459bb0d..c2570b8768 100644 --- a/plugins/auth-backend/src/providers/types.ts +++ b/plugins/auth-backend/src/providers/types.ts @@ -203,6 +203,15 @@ export interface AuthProviderRouteHandlers { * @param {express.Response} res */ logout?(req: express.Request, res: express.Response): Promise; + + /** + *(Optional) A method to identify the environment Context of the Request + * + *Request + *- contains the environment context information encoded in the request + * @param {express.Request} req + */ + identifyEnv?(req: express.Request): string; } export type AuthProviderFactory = (