From 3af994c81f65d40bbb29d381d1ad195d21f4ca2b Mon Sep 17 00:00:00 2001 From: Brian Leathem Date: Mon, 22 Feb 2021 12:42:09 -0800 Subject: [PATCH 1/2] Expose a configuration option for the oidc scope --- .changeset/sweet-experts-whisper.md | 5 +++ .../src/providers/oidc/provider.ts | 45 +++++++++++-------- 2 files changed, 31 insertions(+), 19 deletions(-) create mode 100644 .changeset/sweet-experts-whisper.md diff --git a/.changeset/sweet-experts-whisper.md b/.changeset/sweet-experts-whisper.md new file mode 100644 index 0000000000..ca2f89ceb7 --- /dev/null +++ b/.changeset/sweet-experts-whisper.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Expose a configuration option for the oidc scope diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index 0ec8d0bbf7..c7df8c1e7e 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -55,14 +55,17 @@ type AuthResult = { export type Options = OAuthProviderOptions & { metadataUrl: string; + scope?: string; tokenSignedResponseAlg?: string; }; export class OidcAuthProvider implements OAuthHandlers { private readonly implementation: Promise; + private readonly scope?: string; constructor(options: Options) { this.implementation = this.setupStrategy(options); + this.scope = options.scope; } async start(req: OAuthStartRequest): Promise { @@ -70,7 +73,7 @@ export class OidcAuthProvider implements OAuthHandlers { return await executeRedirectStrategy(req, strategy, { accessType: 'offline', prompt: 'none', - scope: req.scope, + scope: req.scope || this.scope || '', state: encodeState(req.state), }); } @@ -79,28 +82,29 @@ export class OidcAuthProvider implements OAuthHandlers { req: express.Request, ): Promise<{ response: OAuthResponse; refreshToken?: string }> { const { strategy } = await this.implementation; + const strategyResponse = await executeFrameHandlerStrategy< + AuthResult, + PrivateInfo + >(req, strategy); const { result: { userinfo, tokenset }, privateInfo, - } = await executeFrameHandlerStrategy( - req, - strategy, - ); - + } = strategyResponse; + const identityResponse = await this.populateIdentity({ + profile: { + displayName: userinfo.name, + email: userinfo.email, + picture: userinfo.picture, + }, + providerInfo: { + idToken: tokenset.id_token, + accessToken: tokenset.access_token || '', + scope: tokenset.scope || '', + expiresInSeconds: tokenset.expires_in, + }, + }); return { - response: await this.populateIdentity({ - profile: { - displayName: userinfo.name, - email: userinfo.email, - picture: userinfo.picture, - }, - providerInfo: { - idToken: tokenset.id_token, - accessToken: tokenset.access_token || '', - scope: tokenset.scope || '', - expiresInSeconds: tokenset.expires_in, - }, - }), + response: identityResponse, refreshToken: privateInfo.refreshToken, }; } @@ -133,6 +137,7 @@ export class OidcAuthProvider implements OAuthHandlers { redirect_uris: [options.callbackUrl], response_types: ['code'], id_token_signed_response_alg: options.tokenSignedResponseAlg || 'RS256', + scope: options.scope || '', }); const strategy = new OidcStrategy( @@ -188,6 +193,7 @@ export const createOidcProvider = ( const tokenSignedResponseAlg = envConfig.getString( 'tokenSignedResponseAlg', ); + const scope = envConfig.getOptionalString('scope'); const provider = new OidcAuthProvider({ clientId, @@ -195,6 +201,7 @@ export const createOidcProvider = ( callbackUrl, tokenSignedResponseAlg, metadataUrl, + scope, }); return OAuthAdapter.fromConfig(globalConfig, provider, { From f31e1061ca3d0055699e1bc7ff39742789584ef2 Mon Sep 17 00:00:00 2001 From: Brian Leathem Date: Mon, 22 Feb 2021 16:39:06 -0800 Subject: [PATCH 2/2] Replaced the config jest mock with a ConfigReader in the oidc provider test --- .../src/providers/oidc/provider.test.ts | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/plugins/auth-backend/src/providers/oidc/provider.test.ts b/plugins/auth-backend/src/providers/oidc/provider.test.ts index a746c0573b..9a23f701e5 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.test.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.test.ts @@ -21,7 +21,7 @@ import { ClientMetadata, IssuerMetadata } from 'openid-client'; import { createOidcProvider, OidcAuthProvider } from './provider'; import { JWT, JWK } from 'jose'; import { AuthProviderFactoryOptions } from '../types'; -import { Config } from '@backstage/config'; +import { Config, ConfigReader } from '@backstage/config'; import { OAuthAdapter } from '../../lib/oauth'; const issuerMetadata = { @@ -103,23 +103,18 @@ describe('OidcAuthProvider', () => { const scope = nock('https://oidc.test') .get('/.well-known/openid-configuration') .reply(200, issuerMetadata); + const config: Config = new ConfigReader({ + testEnv: { + ...clientMetadata, + metadataUrl: 'https://oidc.test/.well-known/openid-configuration', + }, + }); const options = { globalConfig: { appUrl: 'https://oidc.test', baseUrl: 'https://oidc.test', }, - config: ({ - keys: jest.fn(() => ['test']), - getConfig: jest.fn(() => ({ - getString: (key: string) => { - const conf = { - ...clientMetadata, - metadataUrl: 'https://oidc.test/.well-known/openid-configuration', - } as any; - return conf[key] as string; - }, - })), - } as any) as Config, + config, } as AuthProviderFactoryOptions; const provider = createOidcProvider()(options) as OAuthAdapter; expect(provider.start).toBeDefined();