diff --git a/.changeset/four-phones-shave.md b/.changeset/four-phones-shave.md new file mode 100644 index 0000000000..1ef58bdadb --- /dev/null +++ b/.changeset/four-phones-shave.md @@ -0,0 +1,29 @@ +--- +'@backstage/plugin-auth-backend': minor +--- + +Avoid ever returning OAuth refresh tokens back to the client, and always exchange refresh tokens for a new one when available for all providers. + +This comes with a breaking change to the TypeScript API for custom auth providers. The `refresh` method of `OAuthHandlers` implementation must now return a `{ response, refreshToken }` object rather than a direct response. Existing `refresh` implementations are typically migrated by changing an existing return expression that looks like this: + +```ts +return await this.handleResult({ + fullProfile, + params, + accessToken, + refreshToken, +}); +``` + +Into the following: + +```ts +return { + response: await this.handleResult({ + fullProfile, + params, + accessToken, + }), + refreshToken, +}; +``` diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index 29de38a1f4..af46d874df 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -27,10 +27,13 @@ export class AtlassianAuthProvider implements OAuthHandlers { // (undocumented) handler(req: express.Request): Promise<{ response: OAuthResponse; - refreshToken: string; + refreshToken: string | undefined; }>; // (undocumented) - refresh(req: OAuthRefreshRequest): Promise; + refresh(req: OAuthRefreshRequest): Promise<{ + response: OAuthResponse; + refreshToken: string | undefined; + }>; // Warning: (ae-forgotten-export) The symbol "RedirectInfo" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -488,7 +491,10 @@ export interface OAuthHandlers { // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' - refresh?(req: OAuthRefreshRequest): Promise; + refresh?(req: OAuthRefreshRequest): Promise<{ + response: OAuthResponse; + refreshToken?: string; + }>; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen @@ -503,7 +509,6 @@ export type OAuthProviderInfo = { idToken?: string; expiresInSeconds?: number; scope: string; - refreshToken?: string; }; // Warning: (ae-missing-release-tag) "OAuthProviderOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -703,11 +708,11 @@ export type WebMessageResponse = // // src/identity/types.d.ts:31:9 - (ae-forgotten-export) The symbol "AnyJWK" needs to be exported by the entry point index.d.ts // src/providers/aws-alb/provider.d.ts:77:5 - (ae-forgotten-export) The symbol "AwsAlbResult" needs to be exported by the entry point index.d.ts -// src/providers/github/provider.d.ts:71:58 - (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag -// src/providers/github/provider.d.ts:71:90 - (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag -// src/providers/github/provider.d.ts:71:89 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag -// src/providers/github/provider.d.ts:71:67 - (tsdoc-malformed-html-name) Invalid HTML element: Expecting an HTML name -// src/providers/github/provider.d.ts:71:68 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" -// src/providers/github/provider.d.ts:78:5 - (ae-forgotten-export) The symbol "StateEncoder" needs to be exported by the entry point index.d.ts +// src/providers/github/provider.d.ts:74:58 - (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag +// src/providers/github/provider.d.ts:74:90 - (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag +// src/providers/github/provider.d.ts:74:89 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/providers/github/provider.d.ts:74:67 - (tsdoc-malformed-html-name) Invalid HTML element: Expecting an HTML name +// src/providers/github/provider.d.ts:74:68 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/providers/github/provider.d.ts:81:5 - (ae-forgotten-export) The symbol "StateEncoder" needs to be exported by the entry point index.d.ts // src/providers/types.d.ts:100:5 - (ae-forgotten-export) The symbol "AuthProviderConfig" needs to be exported by the entry point index.d.ts ``` diff --git a/plugins/auth-backend/src/lib/oauth/OAuthAdapter.test.ts b/plugins/auth-backend/src/lib/oauth/OAuthAdapter.test.ts index a3fc77bc02..98f4cd9fa1 100644 --- a/plugins/auth-backend/src/lib/oauth/OAuthAdapter.test.ts +++ b/plugins/auth-backend/src/lib/oauth/OAuthAdapter.test.ts @@ -57,7 +57,10 @@ describe('OAuthAdapter', () => { }; } async refresh() { - return mockResponseData; + return { + response: mockResponseData, + refreshToken: 'token', + }; } } const providerInstance = new MyAuthProvider(); @@ -257,7 +260,10 @@ describe('OAuthAdapter', () => { }); it('correctly populates incomplete identities', async () => { - const mockRefresh = jest.fn, [express.Request]>(); + const mockRefresh = jest.fn< + Promise<{ response: OAuthResponse }>, + [express.Request] + >(); const oauthProvider = new OAuthAdapter( { @@ -291,10 +297,12 @@ describe('OAuthAdapter', () => { // Without a token mockRefresh.mockResolvedValueOnce({ - ...mockResponseData, - backstageIdentity: { - id: 'foo', - token: '', + response: { + ...mockResponseData, + backstageIdentity: { + id: 'foo', + token: '', + }, }, }); await oauthProvider.refresh(mockRequest, mockResponse); @@ -315,10 +323,12 @@ describe('OAuthAdapter', () => { // With a token mockRefresh.mockResolvedValueOnce({ - ...mockResponseData, - backstageIdentity: { - id: 'foo', - token: `z.${mkTokenBody({ sub: 'user:my-ns/foo' })}.z`, + response: { + ...mockResponseData, + backstageIdentity: { + id: 'foo', + token: `z.${mkTokenBody({ sub: 'user:my-ns/foo' })}.z`, + }, }, }); await oauthProvider.refresh(mockRequest, mockResponse); diff --git a/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts b/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts index 4d5d507aa1..ce4b52ef4f 100644 --- a/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts +++ b/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts @@ -212,19 +212,15 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { const forwardReq = Object.assign(req, { scope, refreshToken }); // get new access_token - const response = await this.handlers.refresh( - forwardReq as OAuthRefreshRequest, - ); + const { response, refreshToken: newRefreshToken } = + await this.handlers.refresh(forwardReq as OAuthRefreshRequest); const backstageIdentity = await this.populateIdentity( response.backstageIdentity, ); - if ( - response.providerInfo.refreshToken && - response.providerInfo.refreshToken !== refreshToken - ) { - this.setRefreshTokenCookie(res, response.providerInfo.refreshToken); + if (newRefreshToken && newRefreshToken !== refreshToken) { + this.setRefreshTokenCookie(res, newRefreshToken); } res.status(200).json({ ...response, backstageIdentity }); diff --git a/plugins/auth-backend/src/lib/oauth/types.ts b/plugins/auth-backend/src/lib/oauth/types.ts index cd1439b399..f54d04d15b 100644 --- a/plugins/auth-backend/src/lib/oauth/types.ts +++ b/plugins/auth-backend/src/lib/oauth/types.ts @@ -79,10 +79,6 @@ export type OAuthProviderInfo = { * Scopes granted for the access token. */ scope: string; - /** - * A refresh token issued for the signed in user - */ - refreshToken?: string; }; export type OAuthState = { @@ -130,7 +126,10 @@ export interface OAuthHandlers { * @param {string} refreshToken * @param {string} scope */ - refresh?(req: OAuthRefreshRequest): Promise; + refresh?(req: OAuthRefreshRequest): Promise<{ + response: OAuthResponse; + refreshToken?: string; + }>; /** * (Optional) Sign out of the auth provider. diff --git a/plugins/auth-backend/src/providers/atlassian/provider.test.ts b/plugins/auth-backend/src/providers/atlassian/provider.test.ts index 7bed582f81..29241dd17b 100644 --- a/plugins/auth-backend/src/providers/atlassian/provider.test.ts +++ b/plugins/auth-backend/src/providers/atlassian/provider.test.ts @@ -78,20 +78,22 @@ describe('createAtlassianProvider', () => { refreshToken: 'wacka', }, }); - const { response } = await provider.handler({} as any); - expect(response).toEqual({ - providerInfo: { - accessToken: 'accessToken', - expiresInSeconds: 123, - idToken: 'idToken', - scope: 'scope', - refreshToken: 'wacka', - }, - profile: { - email: 'conrad@example.com', - displayName: 'Conrad', - picture: 'http://google.com/lols', + const result = await provider.handler({} as any); + expect(result).toEqual({ + response: { + providerInfo: { + accessToken: 'accessToken', + expiresInSeconds: 123, + idToken: 'idToken', + scope: 'scope', + }, + profile: { + email: 'conrad@example.com', + displayName: 'Conrad', + picture: 'http://google.com/lols', + }, }, + refreshToken: 'wacka', }); }); @@ -127,20 +129,22 @@ describe('createAtlassianProvider', () => { ], }); - const response = await provider.refresh({} as any); + const result = await provider.refresh({} as any); - expect(response).toEqual({ - profile: { - displayName: 'Mocked User', - email: 'mockuser@gmail.com', - picture: 'http://google.com/lols', - }, - providerInfo: { - accessToken: 'a.b.c', - idToken: 'my-id', - refreshToken: 'dont-forget-to-send-refresh', - scope: 'read_user', + expect(result).toEqual({ + response: { + profile: { + displayName: 'Mocked User', + email: 'mockuser@gmail.com', + picture: 'http://google.com/lols', + }, + providerInfo: { + accessToken: 'a.b.c', + idToken: 'my-id', + scope: 'read_user', + }, }, + refreshToken: 'dont-forget-to-send-refresh', }); }); }); diff --git a/plugins/auth-backend/src/providers/atlassian/provider.ts b/plugins/auth-backend/src/providers/atlassian/provider.ts index e19f29a3a4..ba402eace0 100644 --- a/plugins/auth-backend/src/providers/atlassian/provider.ts +++ b/plugins/auth-backend/src/providers/atlassian/provider.ts @@ -107,9 +107,7 @@ export class AtlassianAuthProvider implements OAuthHandlers { }); } - async handler( - req: express.Request, - ): Promise<{ response: OAuthResponse; refreshToken: string }> { + async handler(req: express.Request) { const { result } = await executeFrameHandlerStrategy( req, this._strategy, @@ -117,7 +115,7 @@ export class AtlassianAuthProvider implements OAuthHandlers { return { response: await this.handleResult(result), - refreshToken: result.refreshToken ?? '', + refreshToken: result.refreshToken, }; } @@ -128,7 +126,6 @@ export class AtlassianAuthProvider implements OAuthHandlers { providerInfo: { idToken: result.params.id_token, accessToken: result.accessToken, - refreshToken: result.refreshToken, scope: result.params.scope, expiresInSeconds: result.params.expires_in, }, @@ -152,28 +149,27 @@ export class AtlassianAuthProvider implements OAuthHandlers { return response; } - async refresh(req: OAuthRefreshRequest): Promise { - const { - accessToken, - params, - refreshToken: newRefreshToken, - } = await executeRefreshTokenStrategy( - this._strategy, - req.refreshToken, - req.scope, - ); + async refresh(req: OAuthRefreshRequest) { + const { accessToken, params, refreshToken } = + await executeRefreshTokenStrategy( + this._strategy, + req.refreshToken, + req.scope, + ); const fullProfile = await executeFetchUserProfileStrategy( this._strategy, accessToken, ); - return this.handleResult({ - fullProfile, - params, - accessToken, - refreshToken: newRefreshToken, - }); + return { + response: await this.handleResult({ + fullProfile, + params, + accessToken, + }), + refreshToken, + }; } } diff --git a/plugins/auth-backend/src/providers/auth0/provider.ts b/plugins/auth-backend/src/providers/auth0/provider.ts index f14d1b8fd5..b99bbe86dd 100644 --- a/plugins/auth-backend/src/providers/auth0/provider.ts +++ b/plugins/auth-backend/src/providers/auth0/provider.ts @@ -113,9 +113,7 @@ export class Auth0AuthProvider implements OAuthHandlers { }); } - async handler( - req: express.Request, - ): Promise<{ response: OAuthResponse; refreshToken: string }> { + async handler(req: express.Request) { const { result, privateInfo } = await executeFrameHandlerStrategy< OAuthResult, PrivateInfo @@ -127,24 +125,27 @@ export class Auth0AuthProvider implements OAuthHandlers { }; } - async refresh(req: OAuthRefreshRequest): Promise { - const { accessToken, params } = await executeRefreshTokenStrategy( - this._strategy, - req.refreshToken, - req.scope, - ); + async refresh(req: OAuthRefreshRequest) { + const { accessToken, refreshToken, params } = + await executeRefreshTokenStrategy( + this._strategy, + req.refreshToken, + req.scope, + ); const fullProfile = await executeFetchUserProfileStrategy( this._strategy, accessToken, ); - return this.handleResult({ - fullProfile, - params, - accessToken, - refreshToken: req.refreshToken, - }); + return { + response: await this.handleResult({ + fullProfile, + params, + accessToken, + }), + refreshToken, + }; } private async handleResult(result: OAuthResult) { diff --git a/plugins/auth-backend/src/providers/bitbucket/provider.ts b/plugins/auth-backend/src/providers/bitbucket/provider.ts index 3a518a6baa..5ad9a739d9 100644 --- a/plugins/auth-backend/src/providers/bitbucket/provider.ts +++ b/plugins/auth-backend/src/providers/bitbucket/provider.ts @@ -138,9 +138,7 @@ export class BitbucketAuthProvider implements OAuthHandlers { }); } - async handler( - req: express.Request, - ): Promise<{ response: OAuthResponse; refreshToken: string }> { + async handler(req: express.Request) { const { result, privateInfo } = await executeFrameHandlerStrategy< OAuthResult, PrivateInfo @@ -152,22 +150,25 @@ export class BitbucketAuthProvider implements OAuthHandlers { }; } - async refresh(req: OAuthRefreshRequest): Promise { - const { accessToken, params } = await executeRefreshTokenStrategy( - this._strategy, - req.refreshToken, - req.scope, - ); + async refresh(req: OAuthRefreshRequest) { + const { accessToken, refreshToken, params } = + await executeRefreshTokenStrategy( + this._strategy, + req.refreshToken, + req.scope, + ); const fullProfile = await executeFetchUserProfileStrategy( this._strategy, accessToken, ); - return this.handleResult({ - fullProfile, - params, - accessToken, - refreshToken: req.refreshToken, - }); + return { + response: await this.handleResult({ + fullProfile, + params, + accessToken, + }), + refreshToken, + }; } private async handleResult(result: BitbucketOAuthResult) { diff --git a/plugins/auth-backend/src/providers/github/provider.test.ts b/plugins/auth-backend/src/providers/github/provider.test.ts index e418ab22c2..b11ac7f5a1 100644 --- a/plugins/auth-backend/src/providers/github/provider.test.ts +++ b/plugins/auth-backend/src/providers/github/provider.test.ts @@ -316,24 +316,26 @@ describe('GithubAuthProvider', () => { ], }); - const response = await provider.refresh({} as any); + const result = await provider.refresh({} as any); - expect(response).toEqual({ - backstageIdentity: { - id: 'mockuser', - token: 'token-for-mockuser', - }, - profile: { - displayName: 'Mocked User', - email: 'mockuser@gmail.com', - picture: undefined, - }, - providerInfo: { - accessToken: 'a.b.c', - refreshToken: 'dont-forget-to-send-refresh', - expiresInSeconds: 123, - scope: 'read_user', + expect(result).toEqual({ + response: { + backstageIdentity: { + id: 'mockuser', + token: 'token-for-mockuser', + }, + profile: { + displayName: 'Mocked User', + email: 'mockuser@gmail.com', + picture: undefined, + }, + providerInfo: { + accessToken: 'a.b.c', + expiresInSeconds: 123, + scope: 'read_user', + }, }, + refreshToken: 'dont-forget-to-send-refresh', }); }); }); diff --git a/plugins/auth-backend/src/providers/github/provider.ts b/plugins/auth-backend/src/providers/github/provider.ts index c7e82cc5ff..b832f4f77b 100644 --- a/plugins/auth-backend/src/providers/github/provider.ts +++ b/plugins/auth-backend/src/providers/github/provider.ts @@ -129,26 +129,26 @@ export class GithubAuthProvider implements OAuthHandlers { }; } - async refresh(req: OAuthRefreshRequest): Promise { - const { - accessToken, - refreshToken: newRefreshToken, - params, - } = await executeRefreshTokenStrategy( - this._strategy, - req.refreshToken, - req.scope, - ); + async refresh(req: OAuthRefreshRequest) { + const { accessToken, refreshToken, params } = + await executeRefreshTokenStrategy( + this._strategy, + req.refreshToken, + req.scope, + ); const fullProfile = await executeFetchUserProfileStrategy( this._strategy, accessToken, ); - return this.handleResult({ - fullProfile, - params, - accessToken, - refreshToken: newRefreshToken, - }); + + return { + response: await this.handleResult({ + fullProfile, + params, + accessToken, + }), + refreshToken, + }; } private async handleResult(result: GithubOAuthResult) { @@ -158,7 +158,6 @@ export class GithubAuthProvider implements OAuthHandlers { const response: OAuthResponse = { providerInfo: { accessToken: result.accessToken, - refreshToken: result.refreshToken, // GitHub expires the old refresh token when used scope: result.params.scope, expiresInSeconds: expiresInStr === undefined ? undefined : Number(expiresInStr), diff --git a/plugins/auth-backend/src/providers/gitlab/provider.test.ts b/plugins/auth-backend/src/providers/gitlab/provider.test.ts index d1a84b43e8..f90de3c75b 100644 --- a/plugins/auth-backend/src/providers/gitlab/provider.test.ts +++ b/plugins/auth-backend/src/providers/gitlab/provider.test.ts @@ -184,23 +184,25 @@ describe('GitlabAuthProvider', () => { ], }); - const response = await provider.refresh({} as any); + const result = await provider.refresh({} as any); - expect(response).toEqual({ - backstageIdentity: { - id: 'mockuser', - }, - profile: { - displayName: 'Mocked User', - email: 'mockuser@gmail.com', - picture: 'http://gitlab.com/lols', - }, - providerInfo: { - accessToken: 'a.b.c', - idToken: 'my-id', - refreshToken: 'dont-forget-to-send-refresh', - scope: 'read_user', + expect(result).toEqual({ + response: { + backstageIdentity: { + id: 'mockuser', + }, + profile: { + displayName: 'Mocked User', + email: 'mockuser@gmail.com', + picture: 'http://gitlab.com/lols', + }, + providerInfo: { + accessToken: 'a.b.c', + idToken: 'my-id', + scope: 'read_user', + }, }, + refreshToken: 'dont-forget-to-send-refresh', }); }); }); diff --git a/plugins/auth-backend/src/providers/gitlab/provider.ts b/plugins/auth-backend/src/providers/gitlab/provider.ts index 6ca405d15f..dedbb7bd5b 100644 --- a/plugins/auth-backend/src/providers/gitlab/provider.ts +++ b/plugins/auth-backend/src/providers/gitlab/provider.ts @@ -132,9 +132,7 @@ export class GitlabAuthProvider implements OAuthHandlers { }); } - async handler( - req: express.Request, - ): Promise<{ response: OAuthResponse; refreshToken: string }> { + async handler(req: express.Request) { const { result, privateInfo } = await executeFrameHandlerStrategy< OAuthResult, PrivateInfo @@ -146,28 +144,26 @@ export class GitlabAuthProvider implements OAuthHandlers { }; } - async refresh(req: OAuthRefreshRequest): Promise { - const { - accessToken, - refreshToken: newRefreshToken, - params, - } = await executeRefreshTokenStrategy( - this._strategy, - req.refreshToken, - req.scope, - ); + async refresh(req: OAuthRefreshRequest) { + const { accessToken, refreshToken, params } = + await executeRefreshTokenStrategy( + this._strategy, + req.refreshToken, + req.scope, + ); const fullProfile = await executeFetchUserProfileStrategy( this._strategy, accessToken, ); - - return this.handleResult({ - fullProfile, - params, - accessToken, - refreshToken: newRefreshToken, - }); + return { + response: await this.handleResult({ + fullProfile, + params, + accessToken, + }), + refreshToken, + }; } private async handleResult(result: OAuthResult): Promise { @@ -177,7 +173,6 @@ export class GitlabAuthProvider implements OAuthHandlers { providerInfo: { idToken: result.params.id_token, accessToken: result.accessToken, - refreshToken: result.refreshToken, // GitLab expires the old refresh token when used scope: result.params.scope, expiresInSeconds: result.params.expires_in, }, diff --git a/plugins/auth-backend/src/providers/google/provider.ts b/plugins/auth-backend/src/providers/google/provider.ts index d85fc2de2d..10123bfe6f 100644 --- a/plugins/auth-backend/src/providers/google/provider.ts +++ b/plugins/auth-backend/src/providers/google/provider.ts @@ -113,9 +113,7 @@ export class GoogleAuthProvider implements OAuthHandlers { }); } - async handler( - req: express.Request, - ): Promise<{ response: OAuthResponse; refreshToken: string }> { + async handler(req: express.Request) { const { result, privateInfo } = await executeFrameHandlerStrategy< OAuthResult, PrivateInfo @@ -127,22 +125,26 @@ export class GoogleAuthProvider implements OAuthHandlers { }; } - async refresh(req: OAuthRefreshRequest): Promise { - const { accessToken, params } = await executeRefreshTokenStrategy( - this._strategy, - req.refreshToken, - req.scope, - ); + async refresh(req: OAuthRefreshRequest) { + const { accessToken, refreshToken, params } = + await executeRefreshTokenStrategy( + this._strategy, + req.refreshToken, + req.scope, + ); const fullProfile = await executeFetchUserProfileStrategy( this._strategy, accessToken, ); - return this.handleResult({ - fullProfile, - params, - accessToken, - refreshToken: req.refreshToken, - }); + + return { + response: await this.handleResult({ + fullProfile, + params, + accessToken, + }), + refreshToken, + }; } private async handleResult(result: OAuthResult) { diff --git a/plugins/auth-backend/src/providers/microsoft/provider.ts b/plugins/auth-backend/src/providers/microsoft/provider.ts index b21218c066..7e928a2786 100644 --- a/plugins/auth-backend/src/providers/microsoft/provider.ts +++ b/plugins/auth-backend/src/providers/microsoft/provider.ts @@ -104,9 +104,7 @@ export class MicrosoftAuthProvider implements OAuthHandlers { }); } - async handler( - req: express.Request, - ): Promise<{ response: OAuthResponse; refreshToken: string }> { + async handler(req: express.Request) { const { result, privateInfo } = await executeFrameHandlerStrategy< OAuthResult, PrivateInfo @@ -118,24 +116,27 @@ export class MicrosoftAuthProvider implements OAuthHandlers { }; } - async refresh(req: OAuthRefreshRequest): Promise { - const { accessToken, params } = await executeRefreshTokenStrategy( - this._strategy, - req.refreshToken, - req.scope, - ); + async refresh(req: OAuthRefreshRequest) { + const { accessToken, refreshToken, params } = + await executeRefreshTokenStrategy( + this._strategy, + req.refreshToken, + req.scope, + ); const fullProfile = await executeFetchUserProfileStrategy( this._strategy, accessToken, ); - return this.handleResult({ - fullProfile, - params, - accessToken, - refreshToken: req.refreshToken, - }); + return { + response: await this.handleResult({ + fullProfile, + params, + accessToken, + }), + refreshToken, + }; } private async handleResult(result: OAuthResult) { diff --git a/plugins/auth-backend/src/providers/oauth2/provider.ts b/plugins/auth-backend/src/providers/oauth2/provider.ts index 2f9c739860..dc4afbac48 100644 --- a/plugins/auth-backend/src/providers/oauth2/provider.ts +++ b/plugins/auth-backend/src/providers/oauth2/provider.ts @@ -127,9 +127,7 @@ export class OAuth2AuthProvider implements OAuthHandlers { }); } - async handler( - req: express.Request, - ): Promise<{ response: OAuthResponse; refreshToken: string }> { + async handler(req: express.Request) { const { result, privateInfo } = await executeFrameHandlerStrategy< OAuthResult, PrivateInfo @@ -141,29 +139,27 @@ export class OAuth2AuthProvider implements OAuthHandlers { }; } - async refresh(req: OAuthRefreshRequest): Promise { + async refresh(req: OAuthRefreshRequest) { const refreshTokenResponse = await executeRefreshTokenStrategy( this._strategy, req.refreshToken, req.scope, ); - const { - accessToken, - params, - refreshToken: updatedRefreshToken, - } = refreshTokenResponse; + const { accessToken, params, refreshToken } = refreshTokenResponse; const fullProfile = await executeFetchUserProfileStrategy( this._strategy, accessToken, ); - return this.handleResult({ - fullProfile, - params, - accessToken, - refreshToken: updatedRefreshToken, - }); + return { + response: await this.handleResult({ + fullProfile, + params, + accessToken, + }), + refreshToken, + }; } private async handleResult(result: OAuthResult) { @@ -175,7 +171,6 @@ export class OAuth2AuthProvider implements OAuthHandlers { accessToken: result.accessToken, scope: result.params.scope, expiresInSeconds: result.params.expires_in, - refreshToken: result.refreshToken, }, profile, }; diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index fe4c042500..e5bfd14f40 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -112,34 +112,31 @@ export class OidcAuthProvider implements OAuthHandlers { return await executeRedirectStrategy(req, strategy, options); } - async handler( - req: express.Request, - ): Promise<{ response: OAuthResponse; refreshToken?: string }> { + async handler(req: express.Request) { const { strategy } = await this.implementation; - const strategyResponse = await executeFrameHandlerStrategy< + const { result, privateInfo } = await executeFrameHandlerStrategy< OidcAuthResult, PrivateInfo >(req, strategy); - const { - result: { userinfo, tokenset }, - privateInfo, - } = strategyResponse; - const identityResponse = await this.handleResult({ tokenset, userinfo }); return { - response: identityResponse, + response: await this.handleResult(result), refreshToken: privateInfo.refreshToken, }; } - async refresh(req: OAuthRefreshRequest): Promise { + async refresh(req: OAuthRefreshRequest) { 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.handleResult({ tokenset, userinfo: profile }); + const userinfo = await client.userinfo(tokenset.access_token); + + return { + response: await this.handleResult({ tokenset, userinfo }), + refreshToken: tokenset.refresh_token, + }; } private async setupStrategy(options: Options): Promise { @@ -190,7 +187,6 @@ export class OidcAuthProvider implements OAuthHandlers { providerInfo: { idToken: result.tokenset.id_token, accessToken: result.tokenset.access_token!, - refreshToken: result.tokenset.refresh_token, scope: result.tokenset.scope!, expiresInSeconds: result.tokenset.expires_in, }, diff --git a/plugins/auth-backend/src/providers/okta/provider.ts b/plugins/auth-backend/src/providers/okta/provider.ts index 69bbbd8c84..1c74e171ad 100644 --- a/plugins/auth-backend/src/providers/okta/provider.ts +++ b/plugins/auth-backend/src/providers/okta/provider.ts @@ -133,9 +133,7 @@ export class OktaAuthProvider implements OAuthHandlers { }); } - async handler( - req: express.Request, - ): Promise<{ response: OAuthResponse; refreshToken: string }> { + async handler(req: express.Request) { const { result, privateInfo } = await executeFrameHandlerStrategy< OAuthResult, PrivateInfo @@ -147,7 +145,7 @@ export class OktaAuthProvider implements OAuthHandlers { }; } - async refresh(req: OAuthRefreshRequest): Promise { + async refresh(req: OAuthRefreshRequest) { const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy( this._strategy, @@ -160,12 +158,14 @@ export class OktaAuthProvider implements OAuthHandlers { accessToken, ); - return this.handleResult({ - fullProfile, - params, - accessToken, + return { + response: await this.handleResult({ + fullProfile, + params, + accessToken, + }), refreshToken, - }); + }; } private async handleResult(result: OAuthResult) { @@ -177,7 +177,6 @@ export class OktaAuthProvider implements OAuthHandlers { accessToken: result.accessToken, scope: result.params.scope, expiresInSeconds: result.params.expires_in, - refreshToken: result.refreshToken, }, profile, }; diff --git a/plugins/auth-backend/src/providers/onelogin/provider.ts b/plugins/auth-backend/src/providers/onelogin/provider.ts index 4f52e31f5b..4b2201e576 100644 --- a/plugins/auth-backend/src/providers/onelogin/provider.ts +++ b/plugins/auth-backend/src/providers/onelogin/provider.ts @@ -112,9 +112,7 @@ export class OneLoginProvider implements OAuthHandlers { }); } - async handler( - req: express.Request, - ): Promise<{ response: OAuthResponse; refreshToken: string }> { + async handler(req: express.Request) { const { result, privateInfo } = await executeFrameHandlerStrategy< OAuthResult, PrivateInfo @@ -126,23 +124,27 @@ export class OneLoginProvider implements OAuthHandlers { }; } - async refresh(req: OAuthRefreshRequest): Promise { - const { accessToken, params } = await executeRefreshTokenStrategy( - this._strategy, - req.refreshToken, - req.scope, - ); + async refresh(req: OAuthRefreshRequest) { + const { accessToken, refreshToken, params } = + await executeRefreshTokenStrategy( + this._strategy, + req.refreshToken, + req.scope, + ); const fullProfile = await executeFetchUserProfileStrategy( this._strategy, accessToken, ); - return this.handleResult({ - fullProfile, - params, - accessToken, - }); + return { + response: await this.handleResult({ + fullProfile, + params, + accessToken, + }), + refreshToken, + }; } private async handleResult(result: OAuthResult) {