Skip to content

Commit 8a368cb

Browse files
authored
feat(passport): logout endpoint for game sdks (#2674)
1 parent d1b3c07 commit 8a368cb

File tree

3 files changed

+56
-13
lines changed

3 files changed

+56
-13
lines changed

packages/passport/sdk/src/Passport.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ export class Passport {
323323
* Returns the logout URL for the current user.
324324
* @returns {Promise<string>} The logout URL
325325
*/
326-
public async getLogoutUrl(): Promise<string> {
326+
public async getLogoutUrl(): Promise<string | null> {
327327
return withMetricsAsync(async () => {
328328
await this.authManager.removeUser();
329329
await this.magicAdapter.logout();

packages/passport/sdk/src/authManager.test.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const clientId = '11111';
2424
const redirectUri = 'https://test.com';
2525
const popupRedirectUri = `${redirectUri}-popup`;
2626
const logoutEndpoint = '/v2/logout';
27+
const crossSdkBridgeLogoutEndpoint = '/im-logged-out';
2728
const logoutRedirectUri = `${redirectUri}logout/callback`;
2829

2930
const getConfig = (values?: Partial<PassportModuleConfiguration>) => new PassportConfiguration({
@@ -108,7 +109,7 @@ describe('AuthManager', () => {
108109
mockOverlayAppend = jest.fn();
109110
mockOverlayRemove = jest.fn();
110111
mockRevokeTokens = jest.fn();
111-
(UserManager as jest.Mock).mockReturnValue({
112+
(UserManager as jest.Mock).mockImplementation((config) => ({
112113
signinPopup: mockSigninPopup,
113114
signinCallback: mockSigninCallback,
114115
signinRedirectCallback: mockSigninRedirectCallback,
@@ -118,7 +119,12 @@ describe('AuthManager', () => {
118119
signinSilent: mockSigninSilent,
119120
storeUser: mockStoreUser,
120121
revokeTokens: mockRevokeTokens,
121-
});
122+
settings: {
123+
metadata: {
124+
end_session_endpoint: config.metadata?.end_session_endpoint,
125+
},
126+
},
127+
}));
122128
(Overlay as jest.Mock).mockReturnValue({
123129
append: mockOverlayAppend,
124130
remove: mockOverlayRemove,
@@ -718,7 +724,9 @@ describe('AuthManager', () => {
718724

719725
const am = new AuthManager(getConfig({ logoutRedirectUri }));
720726
const result = await am.getLogoutUrl();
721-
const uri = new URL(result);
727+
728+
expect(result).not.toBeNull();
729+
const uri = new URL(result!);
722730

723731
expect(uri.hostname).toEqual(authenticationDomain);
724732
expect(uri.pathname).toEqual(logoutEndpoint);
@@ -733,13 +741,43 @@ describe('AuthManager', () => {
733741

734742
const am = new AuthManager(getConfig());
735743
const result = await am.getLogoutUrl();
736-
const uri = new URL(result);
744+
745+
expect(result).not.toBeNull();
746+
const uri = new URL(result!);
737747

738748
expect(uri.hostname).toEqual(authenticationDomain);
739749
expect(uri.pathname).toEqual(logoutEndpoint);
740750
expect(uri.searchParams.get('client_id')).toEqual(clientId);
741751
});
742752
});
753+
754+
describe('when crossSdkBridgeEnabled is true', () => {
755+
it('should use the bridge logout endpoint path', async () => {
756+
mockGetUser.mockReturnValue(mockOidcUser);
757+
758+
const am = new AuthManager(getConfig({ crossSdkBridgeEnabled: true, logoutRedirectUri }));
759+
const result = await am.getLogoutUrl();
760+
761+
expect(result).not.toBeNull();
762+
const uri = new URL(result!);
763+
764+
expect(uri.hostname).toEqual(authenticationDomain);
765+
expect(uri.pathname).toEqual(crossSdkBridgeLogoutEndpoint);
766+
expect(uri.searchParams.get('client_id')).toEqual(clientId);
767+
expect(uri.searchParams.get('returnTo')).toEqual(logoutRedirectUri);
768+
});
769+
});
770+
});
771+
772+
describe('when end_session_endpoint is not available', () => {
773+
it('should return null', async () => {
774+
const am = new AuthManager(getConfig());
775+
// eslint-disable-next-line @typescript-eslint/dot-notation
776+
am['userManager'].settings.metadata!.end_session_endpoint = undefined;
777+
778+
const result = await am.getLogoutUrl();
779+
expect(result).toBeNull();
780+
});
743781
});
744782
});
745783

packages/passport/sdk/src/authManager.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,13 @@ const formUrlEncodedHeader = {
3838
};
3939

4040
const logoutEndpoint = '/v2/logout';
41+
const crossSdkBridgeLogoutEndpoint = '/im-logged-out';
4142
const authorizeEndpoint = '/authorize';
4243

44+
const getLogoutEndpointPath = (crossSdkBridgeEnabled: boolean): string => (
45+
crossSdkBridgeEnabled ? crossSdkBridgeLogoutEndpoint : logoutEndpoint
46+
);
47+
4348
const getAuthConfiguration = (config: PassportConfiguration): UserManagerSettings => {
4449
const { authenticationDomain, oidcConfiguration } = config;
4550

@@ -53,7 +58,7 @@ const getAuthConfiguration = (config: PassportConfiguration): UserManagerSetting
5358
}
5459
const userStore = new WebStorageStateStore({ store });
5560

56-
const endSessionEndpoint = new URL(logoutEndpoint, authenticationDomain.replace(/^(?:https?:\/\/)?(.*)/, 'https://$1'));
61+
const endSessionEndpoint = new URL(getLogoutEndpointPath(config.crossSdkBridgeEnabled), authenticationDomain.replace(/^(?:https?:\/\/)?(.*)/, 'https://$1'));
5762
endSessionEndpoint.searchParams.set('client_id', oidcConfiguration.clientId);
5863
if (oidcConfiguration.logoutRedirectUri) {
5964
endSessionEndpoint.searchParams.set('returnTo', oidcConfiguration.logoutRedirectUri);
@@ -368,15 +373,15 @@ export default class AuthManager {
368373
return this.userManager.removeUser();
369374
}
370375

371-
public async getLogoutUrl(): Promise<string> {
372-
const { authenticationDomain, oidcConfiguration } = this.config;
373-
374-
const endSessionEndpoint = new URL(logoutEndpoint, authenticationDomain);
375-
endSessionEndpoint.searchParams.set('client_id', oidcConfiguration.clientId);
376+
public async getLogoutUrl(): Promise<string | null> {
377+
const endSessionEndpoint = this.userManager.settings?.metadata?.end_session_endpoint;
376378

377-
if (oidcConfiguration.logoutRedirectUri) endSessionEndpoint.searchParams.set('returnTo', oidcConfiguration.logoutRedirectUri);
379+
if (!endSessionEndpoint) {
380+
logger.warn('Failed to get logout URL');
381+
return null;
382+
}
378383

379-
return endSessionEndpoint.toString();
384+
return endSessionEndpoint;
380385
}
381386

382387
public forceUserRefreshInBackground() {

0 commit comments

Comments
 (0)