Skip to content

Commit a7afeb8

Browse files
feat: add test for revoke refresh token on logout (#3716)
1 parent 1df9b3d commit a7afeb8

1 file changed

Lines changed: 77 additions & 32 deletions

File tree

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

Lines changed: 77 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ describe('AuthManager', () => {
9494
let mockStoreUser: jest.Mock;
9595
let mockOverlayAppend: jest.Mock;
9696
let mockOverlayRemove: jest.Mock;
97+
let mockRevokeTokens: jest.Mock;
9798

9899
beforeEach(() => {
99100
mockSigninPopup = jest.fn();
@@ -106,6 +107,7 @@ describe('AuthManager', () => {
106107
mockStoreUser = jest.fn();
107108
mockOverlayAppend = jest.fn();
108109
mockOverlayRemove = jest.fn();
110+
mockRevokeTokens = jest.fn();
109111
(UserManager as jest.Mock).mockReturnValue({
110112
signinPopup: mockSigninPopup,
111113
signinCallback: mockSigninCallback,
@@ -115,6 +117,9 @@ describe('AuthManager', () => {
115117
getUser: mockGetUser,
116118
signinSilent: mockSigninSilent,
117119
storeUser: mockStoreUser,
120+
removeUser: jest.fn(),
121+
clearStaleState: jest.fn(),
122+
revokeTokens: mockRevokeTokens,
118123
});
119124
(Overlay as jest.Mock).mockReturnValue({
120125
append: mockOverlayAppend,
@@ -140,11 +145,13 @@ describe('AuthManager', () => {
140145
userinfo_endpoint: `${config.authenticationDomain}/userinfo`,
141146
end_session_endpoint: `${config.authenticationDomain}${logoutEndpoint}`
142147
+ `?client_id=${config.oidcConfiguration.clientId}`,
148+
revocation_endpoint: `${config.authenticationDomain}/oauth/revoke`,
143149
},
144150
popup_redirect_uri: config.oidcConfiguration.popupRedirectUri,
145151
redirect_uri: config.oidcConfiguration.redirectUri,
146152
scope: config.oidcConfiguration.scope,
147153
userStore: expect.any(WebStorageStateStore),
154+
revokeTokenTypes: ['refresh_token'],
148155
});
149156
});
150157

@@ -383,53 +390,91 @@ describe('AuthManager', () => {
383390
});
384391

385392
describe('logout', () => {
386-
it('should call redirect logout if logout mode is redirect', async () => {
387-
const configuration = getConfig({
388-
logoutMode: 'redirect',
389-
});
390-
const manager = new AuthManager(configuration);
391-
392-
await manager.logout();
393-
394-
expect(mockSignoutRedirect).toBeCalled();
393+
it('should revoke refresh token and call signoutRedirect by default', async () => {
394+
mockGetUser.mockResolvedValue(mockOidcUser);
395+
await authManager.logout();
396+
expect(mockGetUser).toHaveBeenCalledTimes(1);
397+
expect(mockRevokeTokens).toHaveBeenCalledWith(['refresh_token']);
398+
expect(mockSignoutRedirect).toHaveBeenCalledTimes(1);
399+
expect(mockSignoutSilent).not.toHaveBeenCalled();
395400
});
396401

397-
it('should call redirect logout if logout mode is not set', async () => {
398-
const configuration = getConfig({
399-
logoutMode: undefined,
400-
});
401-
const manager = new AuthManager(configuration);
402+
it('should call signoutSilent when logoutMode is silent', async () => {
403+
const silentLogoutConfig = getConfig({ logoutMode: 'silent' });
404+
authManager = new AuthManager(silentLogoutConfig);
405+
mockGetUser.mockResolvedValue(mockOidcUser);
402406

403-
await manager.logout();
407+
await authManager.logout();
404408

405-
expect(mockSignoutRedirect).toBeCalled();
409+
expect(mockGetUser).toHaveBeenCalledTimes(1);
410+
expect(mockRevokeTokens).toHaveBeenCalledWith(['refresh_token']);
411+
expect(mockSignoutSilent).toHaveBeenCalledTimes(1);
412+
expect(mockSignoutRedirect).not.toHaveBeenCalled();
406413
});
407414

408-
it('should call silent logout if logout mode is silent', async () => {
409-
const configuration = getConfig({
410-
logoutMode: 'silent',
411-
});
412-
const manager = new AuthManager(configuration);
415+
it('should not call revokeTokens if user has no refresh token', async () => {
416+
const userWithoutRefreshToken = { ...mockOidcUser, refresh_token: undefined };
417+
mockGetUser.mockResolvedValue(userWithoutRefreshToken);
418+
await authManager.logout();
419+
expect(mockGetUser).toHaveBeenCalledTimes(1);
420+
expect(mockRevokeTokens).not.toHaveBeenCalled();
421+
expect(mockSignoutRedirect).toHaveBeenCalledTimes(1);
422+
});
423+
424+
it('should not call revokeTokens or signout methods if no user is found', async () => {
425+
mockGetUser.mockResolvedValue(null);
426+
await authManager.logout();
427+
expect(mockGetUser).toHaveBeenCalledTimes(1);
428+
expect(mockRevokeTokens).not.toHaveBeenCalled();
429+
expect(mockSignoutRedirect).not.toHaveBeenCalled();
430+
expect(mockSignoutSilent).not.toHaveBeenCalled();
431+
});
413432

414-
await manager.logout();
433+
it('should throw PassportError if revokeTokens fails', async () => {
434+
mockGetUser.mockResolvedValue(mockOidcUser);
435+
const revokeError = new Error('Revoke failed');
436+
mockRevokeTokens.mockRejectedValue(revokeError);
415437

416-
expect(mockSignoutSilent).toBeCalled();
438+
await expect(authManager.logout()).rejects.toThrow(
439+
new PassportError(
440+
revokeError.message,
441+
PassportErrorType.LOGOUT_ERROR,
442+
),
443+
);
444+
expect(mockRevokeTokens).toHaveBeenCalledWith(['refresh_token']);
445+
expect(mockSignoutRedirect).not.toHaveBeenCalled();
417446
});
418447

419-
it('should throw an error if user is failed to logout', async () => {
420-
const configuration = getConfig({
421-
logoutMode: 'redirect',
422-
});
423-
const manager = new AuthManager(configuration);
448+
it('should throw PassportError if signoutRedirect fails', async () => {
449+
mockGetUser.mockResolvedValue(mockOidcUser);
450+
const signOutError = new Error('Signout failed');
451+
mockSignoutRedirect.mockRejectedValue(signOutError);
452+
453+
await expect(authManager.logout()).rejects.toThrow(
454+
new PassportError(
455+
signOutError.message,
456+
PassportErrorType.LOGOUT_ERROR,
457+
),
458+
);
459+
expect(mockRevokeTokens).toHaveBeenCalledWith(['refresh_token']);
460+
expect(mockSignoutRedirect).toHaveBeenCalledTimes(1);
461+
});
424462

425-
mockSignoutRedirect.mockRejectedValue(new Error(mockErrorMsg));
463+
it('should throw PassportError if signoutSilent fails', async () => {
464+
const silentLogoutConfig = getConfig({ logoutMode: 'silent' });
465+
authManager = new AuthManager(silentLogoutConfig);
466+
mockGetUser.mockResolvedValue(mockOidcUser);
467+
const signOutError = new Error('Signout silent failed');
468+
mockSignoutSilent.mockRejectedValue(signOutError);
426469

427-
await expect(() => manager.logout()).rejects.toThrow(
470+
await expect(authManager.logout()).rejects.toThrow(
428471
new PassportError(
429-
mockErrorMsg,
472+
signOutError.message,
430473
PassportErrorType.LOGOUT_ERROR,
431474
),
432475
);
476+
expect(mockRevokeTokens).toHaveBeenCalledWith(['refresh_token']);
477+
expect(mockSignoutSilent).toHaveBeenCalledTimes(1);
433478
});
434479
});
435480

@@ -473,7 +518,7 @@ describe('AuthManager', () => {
473518

474519
await expect(() => authManager.getUser()).rejects.toThrow(
475520
new PassportError(
476-
'Failed to refresh token: oops: Failed to remove user: this.userManager.removeUser is not a function',
521+
'Failed to refresh token: oops',
477522
PassportErrorType.AUTHENTICATION_ERROR,
478523
),
479524
);

0 commit comments

Comments
 (0)