Skip to content

Commit 36de0bb

Browse files
committed
fix(auth): refreshToken unawaited async operation caused race condition
1 parent bd73367 commit 36de0bb

File tree

2 files changed

+77
-1
lines changed

2 files changed

+77
-1
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { Hub } from '@aws-amplify/core';
5+
import { assertTokenProviderConfig } from '@aws-amplify/core/internals/utils';
6+
import { tokenOrchestrator } from '../../../../src/providers/cognito/tokenProvider';
7+
import { CognitoAuthTokens } from '../../../../src/providers/cognito/tokenProvider/types';
8+
9+
jest.mock('@aws-amplify/core/internals/utils');
10+
jest.mock('@aws-amplify/core', () => ({
11+
...jest.requireActual('@aws-amplify/core'),
12+
Hub: {
13+
dispatch: jest.fn(),
14+
},
15+
}));
16+
17+
const mockAssertTokenProviderConfig = assertTokenProviderConfig as jest.Mock;
18+
19+
describe('tokenOrchestrator', () => {
20+
const mockTokenRefresher = jest.fn();
21+
const mockTokenStore = {
22+
storeTokens: jest.fn(),
23+
getLastAuthUser: jest.fn(),
24+
loadTokens: jest.fn(),
25+
clearTokens: jest.fn(),
26+
setKeyValueStorage: jest.fn(),
27+
getDeviceMetadata: jest.fn(),
28+
clearDeviceMetadata: jest.fn(),
29+
};
30+
31+
beforeAll(() => {
32+
tokenOrchestrator.setTokenRefresher(mockTokenRefresher);
33+
tokenOrchestrator.setAuthTokenStore(mockTokenStore);
34+
});
35+
36+
describe('refreshTokens method', () => {
37+
it('calls the set tokenRefresher, tokenStore and Hub while refreshing tokens', async () => {
38+
const testUsername = 'username';
39+
const testInputTokens = {
40+
accessToken: {
41+
payload: {},
42+
},
43+
clockDrift: 400000,
44+
username: testUsername,
45+
};
46+
47+
const mockTokens: CognitoAuthTokens = {
48+
accessToken: {
49+
payload: {},
50+
},
51+
clockDrift: 300,
52+
username: testUsername,
53+
};
54+
mockTokenRefresher.mockResolvedValueOnce(mockTokens);
55+
mockTokenStore.storeTokens.mockResolvedValue(void 0);
56+
const newTokens = await tokenOrchestrator['refreshTokens']({
57+
tokens: testInputTokens,
58+
username: testUsername,
59+
});
60+
61+
// ensure the underlying async operations to be completed
62+
// async #1
63+
expect(mockTokenRefresher).toHaveBeenCalledWith(
64+
expect.objectContaining({
65+
tokens: testInputTokens,
66+
username: testUsername,
67+
})
68+
);
69+
// async #2
70+
expect(mockTokenStore.storeTokens).toHaveBeenCalledWith(mockTokens);
71+
72+
// ensure the result is correct
73+
expect(newTokens).toEqual(mockTokens);
74+
});
75+
});
76+
});

packages/auth/src/providers/cognito/tokenProvider/TokenOrchestrator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export class TokenOrchestrator implements AuthTokenOrchestrator {
124124
username,
125125
});
126126

127-
this.setTokens({ tokens: newTokens });
127+
await this.setTokens({ tokens: newTokens });
128128
Hub.dispatch('auth', { event: 'tokenRefresh' }, 'Auth', AMPLIFY_SYMBOL);
129129

130130
return newTokens;

0 commit comments

Comments
 (0)