diff --git a/packages/game-bridge/src/index.ts b/packages/game-bridge/src/index.ts index 3b0e0db114..028465f78d 100644 --- a/packages/game-bridge/src/index.ts +++ b/packages/game-bridge/src/index.ts @@ -38,6 +38,7 @@ const PASSPORT_FUNCTIONS = { connectPKCE: 'connectPKCE', getAccessToken: 'getAccessToken', getIdToken: 'getIdToken', + logout: 'logout', getEmail: 'getEmail', getPassportId: 'getPassportId', getLinkedAddresses: 'getLinkedAddresses', @@ -402,6 +403,20 @@ window.callFunction = async (jsonData: string) => { }); break; } + case PASSPORT_FUNCTIONS.logout: { + const logoutUrl = await getPassportClient().getLogoutUrl(); + providerInstance = null; + zkEvmProviderInstance = null; + trackDuration(moduleName, 'performedGetLogoutUrl', mt(markStart)); + callbackToGame({ + responseFor: fxName, + requestId, + success: true, + error: null, + result: logoutUrl, + }); + break; + } case PASSPORT_FUNCTIONS.getAccessToken: { const accessToken = await getPassportClient().getAccessToken(); const success = accessToken !== undefined; diff --git a/packages/passport/sdk/src/Passport.ts b/packages/passport/sdk/src/Passport.ts index 50a87a38f0..98b657345e 100644 --- a/packages/passport/sdk/src/Passport.ts +++ b/packages/passport/sdk/src/Passport.ts @@ -314,6 +314,19 @@ export class Passport { }, 'logout'); } + /** + * Returns the logout URL for the current user. + * @returns {Promise} The logout URL + */ + public async getLogoutUrl(): Promise { + return withMetricsAsync(async () => { + await this.authManager.removeUser(); + await this.magicAdapter.logout(); + this.passportEventEmitter.emit(PassportEvents.LOGGED_OUT); + return await this.authManager.getLogoutUrl(); + }, 'getLogoutUrl'); + } + /** * Handles the silent logout callback. * @param {string} url - The callback URL to process diff --git a/packages/passport/sdk/src/authManager.test.ts b/packages/passport/sdk/src/authManager.test.ts index 209fccdaa0..8b5aabd583 100644 --- a/packages/passport/sdk/src/authManager.test.ts +++ b/packages/passport/sdk/src/authManager.test.ts @@ -659,4 +659,37 @@ describe('AuthManager', () => { ); }); }); + + describe('getLogoutUrl', () => { + describe('with a logged in user', () => { + describe('when a logoutRedirectUri is specified', () => { + it('should set the endSessionEndpoint `returnTo` and `client_id` query string params', async () => { + mockGetUser.mockReturnValue(mockOidcUser); + + const am = new AuthManager(getConfig({ logoutRedirectUri })); + const result = await am.getLogoutUrl(); + const uri = new URL(result); + + expect(uri.hostname).toEqual(authenticationDomain); + expect(uri.pathname).toEqual(logoutEndpoint); + expect(uri.searchParams.get('client_id')).toEqual(clientId); + expect(uri.searchParams.get('returnTo')).toEqual(logoutRedirectUri); + }); + }); + + describe('when no post_logout_redirect_uri is specified', () => { + it('should return the endSessionEndpoint without a `returnTo` or `client_id` query string params', async () => { + mockGetUser.mockReturnValue(mockOidcUser); + + const am = new AuthManager(getConfig()); + const result = await am.getLogoutUrl(); + const uri = new URL(result); + + expect(uri.hostname).toEqual(authenticationDomain); + expect(uri.pathname).toEqual(logoutEndpoint); + expect(uri.searchParams.get('client_id')).toEqual(clientId); + }); + }); + }); + }); }); diff --git a/packages/passport/sdk/src/authManager.ts b/packages/passport/sdk/src/authManager.ts index 4eba9d222c..c6bc92a448 100644 --- a/packages/passport/sdk/src/authManager.ts +++ b/packages/passport/sdk/src/authManager.ts @@ -364,6 +364,17 @@ export default class AuthManager { return this.userManager.removeUser(); } + public async getLogoutUrl(): Promise { + const { authenticationDomain, oidcConfiguration } = this.config; + + const endSessionEndpoint = new URL(logoutEndpoint, authenticationDomain); + endSessionEndpoint.searchParams.set('client_id', oidcConfiguration.clientId); + + if (oidcConfiguration.logoutRedirectUri) endSessionEndpoint.searchParams.set('returnTo', oidcConfiguration.logoutRedirectUri); + + return endSessionEndpoint.toString(); + } + public forceUserRefreshInBackground() { this.refreshTokenAndUpdatePromise().catch((error) => { logger.warn('Failed to refresh user token', error);