diff --git a/plugins/auth-backend/src/providers/oidc/provider.test.ts b/plugins/auth-backend/src/providers/oidc/provider.test.ts index ade69255b7..f2bd8dcb90 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.test.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.test.ts @@ -54,9 +54,11 @@ describe('OidcAuthProvider', () => { .get('/.well-known/openid-configuration') .reply(200, issuerMetadata); const provider = new OidcAuthProvider(clientMetadata); - const strategy = ((await provider._strategy) as any) as { - _client: ClientMetadata; - _issuer: IssuerMetadata; + const { strategy } = ((await (provider as any).implementation) as any) as { + strategy: { + _client: ClientMetadata; + _issuer: IssuerMetadata; + }; }; // Assert that the expected request to the metadaurl was made. expect(scope.isDone()).toBeTruthy(); @@ -89,27 +91,38 @@ describe('OidcAuthProvider', () => { const provider = new OidcAuthProvider(clientMetadata); const req = { method: 'GET', - url: '/?code=test2', + url: 'https://oidc.test/?code=test2', session: ({ 'oidc:oidc.test': 'test' } as any) as Session, } as express.Request; await provider.handler(req); expect(scope.isDone()).toBeTruthy(); }); - const options = { - globalConfig: { - appUrl: 'https://oidc.test', - baseUrl: 'https://oidc.test', - }, - config: ({ - keys: jest.fn(() => ['test']), - getConfig: jest.fn(() => ({ getString: () => '' })), - } as any) as Config, - } as AuthProviderFactoryOptions; - - it('createOidcProvider', () => { + it('createOidcProvider', async () => { + const scope = nock('https://oidc.test') + .get('/.well-known/openid-configuration') + .reply(200, issuerMetadata); + 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, + } as AuthProviderFactoryOptions; const provider = createOidcProvider(options) as OAuthAdapter; - console.log(provider); expect(provider.start).toBeDefined(); + await new Promise(resolve => process.nextTick(resolve)); // advance a tick to give nock a chance to intercept the request + expect(scope.isDone()).toBeTruthy(); }); }); diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index 5ebb859f2f..fbf38c69c4 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -33,10 +33,8 @@ import { OAuthRefreshRequest, } from '../../lib/oauth'; import { - executeFetchUserProfileStrategy, executeFrameHandlerStrategy, executeRedirectStrategy, - executeRefreshTokenStrategy, PassportDoneCallback, } from '../../lib/passport'; import { RedirectInfo, AuthProviderFactory, ProfileInfo } from '../types'; @@ -45,20 +43,25 @@ type PrivateInfo = { refreshToken: string; }; +type OidcImpl = { + strategy: OidcStrategy; + client: Client; +}; + export type OidcAuthProviderOptions = OAuthProviderOptions & { metadataUrl: string; tokenSignedResponseAlg?: string; }; export class OidcAuthProvider implements OAuthHandlers { - readonly _strategy: Promise>; + private readonly implementation: Promise; constructor(options: OidcAuthProviderOptions) { - this._strategy = this.setupStrategy(options); + this.implementation = this.setupStrategy(options); } async start(req: OAuthStartRequest): Promise { - const strategy = await this._strategy; + const { strategy } = await this.implementation; return await executeRedirectStrategy(req, strategy, { accessType: 'offline', prompt: 'none', @@ -70,7 +73,7 @@ export class OidcAuthProvider implements OAuthHandlers { async handler( req: express.Request, ): Promise<{ response: OAuthResponse; refreshToken: string }> { - const strategy = await this._strategy; + const { strategy } = await this.implementation; const { response, privateInfo } = await executeFrameHandlerStrategy< OAuthResponse, PrivateInfo @@ -83,31 +86,20 @@ export class OidcAuthProvider implements OAuthHandlers { } async refresh(req: OAuthRefreshRequest): Promise { - const strategy = await this._strategy; - const refreshTokenResponse = await executeRefreshTokenStrategy( - strategy, - req.refreshToken, - req.scope, - ); - const { - accessToken, - params, - refreshToken: updatedRefreshToken, - } = refreshTokenResponse; - - const profile = await executeFetchUserProfileStrategy( - strategy, - accessToken, - params.id_token, - ); + const { client } = await this.implementation; + const tokenset = await client.refresh(req.refreshToken); + if (!tokenset.access_token) { + throw new Error('Refresh failed'); + } + const profile = await client.userinfo(tokenset.access_token); return this.populateIdentity({ providerInfo: { - accessToken, - refreshToken: updatedRefreshToken, - idToken: params.id_token, - expiresInSeconds: params.expires_in, - scope: params.scope, + accessToken: tokenset.access_token, + refreshToken: tokenset.refresh_token, + expiresInSeconds: tokenset.expires_in, + idToken: tokenset.id_token, + scope: tokenset.scope || '', }, profile, }); @@ -115,7 +107,7 @@ export class OidcAuthProvider implements OAuthHandlers { private async setupStrategy( options: OidcAuthProviderOptions, - ): Promise> { + ): Promise { const issuer = await Issuer.discover(options.metadataUrl); const client = new issuer.Client({ client_id: options.clientId, @@ -159,7 +151,7 @@ export class OidcAuthProvider implements OAuthHandlers { }, ); strategy.error = console.error; - return strategy; + return { strategy, client }; } // Use this function to grab the user profile info from the token