Skip to content

Commit af17f65

Browse files
authored
fix behavioral change (#28)
1 parent f84faec commit af17f65

2 files changed

Lines changed: 24 additions & 5 deletions

File tree

src/authentication/authenticationContext.spec.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,10 @@ describe('AuthenticationContext', function() {
103103
expect(await authenticationContext.isSignedIn()).toBe(false);
104104
});
105105

106-
it('completes sign-out even when getIdToken throws due to expired token with no refresh source', async () => {
106+
it('completes sign-out when user is not signed in (getIdToken returns null for anonymous user)', async () => {
107107
(authenticationContext as any).accessTokenResponse = { isValid: () => false };
108108

109-
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
110109
await authenticationContext.signOut();
111-
warnSpy.mockRestore();
112110

113111
expect(await authenticationContext.isSignedIn()).toBe(false);
114112
});
@@ -304,6 +302,15 @@ describe('AuthenticationContext', function() {
304302
expect(accessToken).toBe(fakeAccessToken);
305303
});
306304

305+
it('returns null when access token is expired and no refresh source is available (anonymous user)', async () => {
306+
(authenticationContext as any).accessTokenResponse = { isValid: () => false };
307+
// No refresh token in localStorage, no refreshAccessToken callback
308+
309+
const token = await authenticationContext.getAccessToken();
310+
311+
expect(token).toBeNull();
312+
});
313+
307314
it('refreshes the accessToken if it is no longer valid', async () => {
308315
(authenticationContext as any).accessTokenResponse = { isValid: () => false };
309316
localStorage.setItem('elfsquad_refresh_token', 'STORED_TOKEN');
@@ -615,6 +622,15 @@ describe('AuthenticationContext', function() {
615622

616623
describe('getIdToken', function() {
617624

625+
it('returns null when access token is expired and no refresh source is available (anonymous user)', async () => {
626+
(authenticationContext as any).accessTokenResponse = { isValid: () => false };
627+
// No refresh token in localStorage, no refreshAccessToken callback
628+
629+
const idToken = await authenticationContext.getIdToken();
630+
631+
expect(idToken).toBeNull();
632+
});
633+
618634
it('returns the id token after refreshing when the access token is expired', async () => {
619635
const refreshMock = jest.fn().mockResolvedValue({ accessToken: 'NEW_TOKEN', expiresIn: 3600, idToken: 'NEW_ID_TOKEN' });
620636
authenticationContext = new AuthenticationContext({

src/authentication/authenticationContext.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ export class AuthenticationContext {
249249
}
250250

251251
if (!this.options.refreshAccessToken && !TokenStore.hasRefreshToken()) {
252-
throw new Error('@elfsquad/authentication: Access token expired and no refresh source is available. Ensure offline_access is in the requested scope or provide a refreshAccessToken callback.');
252+
return null;
253253
}
254254

255255
if (this._refreshTokenPromise) {
@@ -291,7 +291,10 @@ export class AuthenticationContext {
291291
// Delegate to getAccessToken() so the shared _refreshTokenPromise gate is used,
292292
// preventing duplicate refresh calls when getAccessToken() and getIdToken() are
293293
// awaited concurrently.
294-
await this.getAccessToken();
294+
const accessToken = await this.getAccessToken();
295+
if (!accessToken) {
296+
return null;
297+
}
295298
return this.accessTokenResponse.idToken;
296299
}
297300

0 commit comments

Comments
 (0)