Skip to content

Commit 1b8a803

Browse files
committed
feat(12820): implement crossTab auth events
1 parent b29d394 commit 1b8a803

File tree

15 files changed

+363
-30
lines changed

15 files changed

+363
-30
lines changed

packages/auth/__tests__/providers/cognito/tokenProvider/tokenStore.test.ts

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
import { KeyValueStorageInterface } from '@aws-amplify/core';
5-
import { decodeJWT } from '@aws-amplify/core/internals/utils';
4+
import { Hub, KeyValueStorageInterface } from '@aws-amplify/core';
5+
import { AMPLIFY_SYMBOL, decodeJWT } from '@aws-amplify/core/internals/utils';
66

7-
import { DefaultTokenStore } from '../../../../src/providers/cognito/tokenProvider';
7+
import {
8+
AUTH_KEY_PREFIX,
9+
DefaultTokenStore,
10+
} from '../../../../src/providers/cognito/tokenProvider';
811

912
const userPoolId = 'us-west-1:0000523';
1013
const userPoolClientId = 'mockCognitoUserPoolsId';
@@ -21,6 +24,9 @@ jest.mock(
2124
TokenProviderErrorCode: 'mockErrorCode',
2225
}),
2326
);
27+
jest.mock('../../../../src/providers/cognito/apis/getCurrentUser', () => ({
28+
getCurrentUser: () => Promise.resolve({}),
29+
}));
2430

2531
const mockedDecodeJWT = jest.mocked(decodeJWT);
2632
mockedDecodeJWT.mockReturnValue({
@@ -72,6 +78,7 @@ const mockKeyValueStorage: jest.Mocked<KeyValueStorageInterface> = {
7278
getItem: jest.fn(),
7379
removeItem: jest.fn(),
7480
clear: jest.fn(),
81+
addListener: jest.fn(),
7582
};
7683

7784
describe('TokenStore', () => {
@@ -403,4 +410,83 @@ describe('TokenStore', () => {
403410
expect(finalTokens?.refreshToken).toBe(newMockAuthToken.refreshToken);
404411
});
405412
});
413+
414+
describe('setupNotify', () => {
415+
it('should setup a KeyValueStorageEvent listener', async () => {
416+
tokenStore.setupNotify();
417+
418+
const spy = jest.spyOn(keyValStorage, 'addListener');
419+
const hubSpy = jest.spyOn(Hub, 'dispatch');
420+
421+
expect(spy).toHaveBeenCalledWith(expect.any(Function));
422+
423+
const listener = spy.mock.calls[0][0];
424+
425+
// does nothing if key does not match
426+
await listener({
427+
key: 'foo.bar',
428+
oldValue: null,
429+
newValue: null,
430+
});
431+
432+
expect(hubSpy).not.toHaveBeenCalled();
433+
434+
// does nothing if both values are null
435+
await listener({
436+
key: `${AUTH_KEY_PREFIX}.someid.someotherId.refreshToken`,
437+
oldValue: null,
438+
newValue: null,
439+
});
440+
441+
expect(hubSpy).not.toHaveBeenCalled();
442+
443+
// dispatches signedIn on new value
444+
await listener({
445+
key: `${AUTH_KEY_PREFIX}.someid.someotherId.refreshToken`,
446+
newValue: '123',
447+
oldValue: null,
448+
});
449+
450+
expect(hubSpy).toHaveBeenCalledWith(
451+
'auth',
452+
{ event: 'signedIn', data: {} },
453+
'Auth',
454+
AMPLIFY_SYMBOL,
455+
true,
456+
);
457+
hubSpy.mockClear();
458+
459+
// dispatches signedOut on null newValue
460+
await listener({
461+
key: `${AUTH_KEY_PREFIX}.someid.someotherId.refreshToken`,
462+
newValue: null,
463+
oldValue: '123',
464+
});
465+
466+
expect(hubSpy).toHaveBeenCalledWith(
467+
'auth',
468+
{ event: 'signedOut' },
469+
'Auth',
470+
AMPLIFY_SYMBOL,
471+
true,
472+
);
473+
hubSpy.mockClear();
474+
475+
// dispatches tokenRefresh for changed value
476+
await listener({
477+
key: `${AUTH_KEY_PREFIX}.someid.someotherId.refreshToken`,
478+
newValue: '456',
479+
oldValue: '123',
480+
});
481+
482+
expect(hubSpy).toHaveBeenCalledWith(
483+
'auth',
484+
{ event: 'tokenRefresh' },
485+
'Auth',
486+
AMPLIFY_SYMBOL,
487+
true,
488+
);
489+
hubSpy.mockClear();
490+
});
491+
});
406492
});

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export class CognitoUserPoolsTokenProvider
2323
constructor() {
2424
this.authTokenStore = new DefaultTokenStore();
2525
this.authTokenStore.setKeyValueStorage(defaultStorage);
26+
this.authTokenStore.setupNotify();
2627
this.tokenOrchestrator = new TokenOrchestrator();
2728
this.tokenOrchestrator.setAuthTokenStore(this.authTokenStore);
2829
this.tokenOrchestrator.setTokenRefresher(refreshAuthTokens);

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
3-
import { AuthConfig, KeyValueStorageInterface } from '@aws-amplify/core';
3+
import { AuthConfig, Hub, KeyValueStorageInterface } from '@aws-amplify/core';
44
import {
5+
AMPLIFY_SYMBOL,
56
assertTokenProviderConfig,
67
decodeJWT,
78
} from '@aws-amplify/core/internals/utils';
9+
import { KeyValueStorageEvent } from '@aws-amplify/core/src/types';
810

911
import { AuthError } from '../../../errors/AuthError';
12+
import { getCurrentUser } from '../apis/getCurrentUser';
1013

1114
import {
1215
AuthKeys,
@@ -42,6 +45,47 @@ export class DefaultTokenStore implements AuthTokenStore {
4245
this.authConfig = authConfig;
4346
}
4447

48+
setupNotify() {
49+
this.keyValueStorage?.addListener?.(async (e: KeyValueStorageEvent) => {
50+
const [key, , , id] = (e.key || '').split('.');
51+
if (key === AUTH_KEY_PREFIX && id === 'refreshToken') {
52+
const { newValue, oldValue } = e;
53+
if (newValue && oldValue === null) {
54+
Hub.dispatch(
55+
'auth',
56+
{
57+
event: 'signedIn',
58+
data: await getCurrentUser(),
59+
},
60+
'Auth',
61+
AMPLIFY_SYMBOL,
62+
true,
63+
);
64+
} else if (newValue === null && oldValue) {
65+
Hub.dispatch(
66+
'auth',
67+
{
68+
event: 'signedOut',
69+
},
70+
'Auth',
71+
AMPLIFY_SYMBOL,
72+
true,
73+
);
74+
} else if (newValue && oldValue) {
75+
Hub.dispatch(
76+
'auth',
77+
{
78+
event: 'tokenRefresh',
79+
},
80+
'Auth',
81+
AMPLIFY_SYMBOL,
82+
true,
83+
);
84+
}
85+
}
86+
});
87+
}
88+
4589
async loadTokens(): Promise<CognitoAuthTokens | null> {
4690
// TODO(v6): migration logic should be here
4791
// Reading V5 tokens old format

packages/auth/src/providers/cognito/utils/signInWithRedirectStore.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ import {
77
} from '@aws-amplify/core';
88
import { assertTokenProviderConfig } from '@aws-amplify/core/internals/utils';
99

10+
import { AUTH_KEY_PREFIX } from '../tokenProvider/constants';
1011
import { getAuthStorageKeys } from '../tokenProvider/TokenStore';
1112

1213
import { OAuthStorageKeys, OAuthStore } from './types';
1314

1415
const V5_HOSTED_UI_KEY = 'amplify-signin-with-hostedUI';
1516

16-
const name = 'CognitoIdentityServiceProvider';
1717
export class DefaultOAuthStore implements OAuthStore {
1818
keyValueStorage: KeyValueStorageInterface;
1919
cognitoConfig?: CognitoUserPoolConfig;
@@ -26,7 +26,7 @@ export class DefaultOAuthStore implements OAuthStore {
2626
assertTokenProviderConfig(this.cognitoConfig);
2727

2828
const authKeys = createKeysForAuthStorage(
29-
name,
29+
AUTH_KEY_PREFIX,
3030
this.cognitoConfig.userPoolClientId,
3131
);
3232
await Promise.all([
@@ -39,7 +39,7 @@ export class DefaultOAuthStore implements OAuthStore {
3939
async clearOAuthData(): Promise<void> {
4040
assertTokenProviderConfig(this.cognitoConfig);
4141
const authKeys = createKeysForAuthStorage(
42-
name,
42+
AUTH_KEY_PREFIX,
4343
this.cognitoConfig.userPoolClientId,
4444
);
4545
await this.clearOAuthInflightData();
@@ -52,7 +52,7 @@ export class DefaultOAuthStore implements OAuthStore {
5252
assertTokenProviderConfig(this.cognitoConfig);
5353

5454
const authKeys = createKeysForAuthStorage(
55-
name,
55+
AUTH_KEY_PREFIX,
5656
this.cognitoConfig.userPoolClientId,
5757
);
5858

@@ -63,7 +63,7 @@ export class DefaultOAuthStore implements OAuthStore {
6363
assertTokenProviderConfig(this.cognitoConfig);
6464

6565
const authKeys = createKeysForAuthStorage(
66-
name,
66+
AUTH_KEY_PREFIX,
6767
this.cognitoConfig.userPoolClientId,
6868
);
6969

@@ -74,7 +74,7 @@ export class DefaultOAuthStore implements OAuthStore {
7474
assertTokenProviderConfig(this.cognitoConfig);
7575

7676
const authKeys = createKeysForAuthStorage(
77-
name,
77+
AUTH_KEY_PREFIX,
7878
this.cognitoConfig.userPoolClientId,
7979
);
8080

@@ -85,7 +85,7 @@ export class DefaultOAuthStore implements OAuthStore {
8585
assertTokenProviderConfig(this.cognitoConfig);
8686

8787
const authKeys = createKeysForAuthStorage(
88-
name,
88+
AUTH_KEY_PREFIX,
8989
this.cognitoConfig.userPoolClientId,
9090
);
9191

@@ -100,7 +100,7 @@ export class DefaultOAuthStore implements OAuthStore {
100100
assertTokenProviderConfig(this.cognitoConfig);
101101

102102
const authKeys = createKeysForAuthStorage(
103-
name,
103+
AUTH_KEY_PREFIX,
104104
this.cognitoConfig.userPoolClientId,
105105
);
106106

@@ -112,7 +112,7 @@ export class DefaultOAuthStore implements OAuthStore {
112112
async storeOAuthInFlight(inflight: boolean): Promise<void> {
113113
assertTokenProviderConfig(this.cognitoConfig);
114114
const authKeys = createKeysForAuthStorage(
115-
name,
115+
AUTH_KEY_PREFIX,
116116
this.cognitoConfig.userPoolClientId,
117117
);
118118

@@ -126,7 +126,7 @@ export class DefaultOAuthStore implements OAuthStore {
126126
assertTokenProviderConfig(this.cognitoConfig);
127127

128128
const authKeys = createKeysForAuthStorage(
129-
name,
129+
AUTH_KEY_PREFIX,
130130
this.cognitoConfig.userPoolClientId,
131131
);
132132

@@ -151,7 +151,7 @@ export class DefaultOAuthStore implements OAuthStore {
151151
assertTokenProviderConfig(this.cognitoConfig);
152152

153153
const authKeys = createKeysForAuthStorage(
154-
name,
154+
AUTH_KEY_PREFIX,
155155
this.cognitoConfig.userPoolClientId,
156156
);
157157

packages/aws-amplify/package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@
307307
"name": "[Analytics] record (Pinpoint)",
308308
"path": "./dist/esm/analytics/index.mjs",
309309
"import": "{ record }",
310-
"limit": "18.10 kB"
310+
"limit": "18.17 kB"
311311
},
312312
{
313313
"name": "[Analytics] record (Kinesis)",
@@ -427,19 +427,19 @@
427427
"name": "[Auth] setUpTOTP (Cognito)",
428428
"path": "./dist/esm/auth/index.mjs",
429429
"import": "{ setUpTOTP }",
430-
"limit": "14 kB"
430+
"limit": "14.08 kB"
431431
},
432432
{
433433
"name": "[Auth] updateUserAttributes (Cognito)",
434434
"path": "./dist/esm/auth/index.mjs",
435435
"import": "{ updateUserAttributes }",
436-
"limit": "13 kB"
436+
"limit": "13.01 kB"
437437
},
438438
{
439439
"name": "[Auth] getCurrentUser (Cognito)",
440440
"path": "./dist/esm/auth/index.mjs",
441441
"import": "{ getCurrentUser }",
442-
"limit": "8.30 kB"
442+
"limit": "8.37 kB"
443443
},
444444
{
445445
"name": "[Auth] confirmUserAttribute (Cognito)",
@@ -493,7 +493,7 @@
493493
"name": "[Storage] copy (S3)",
494494
"path": "./dist/esm/storage/index.mjs",
495495
"import": "{ copy }",
496-
"limit": "17 kB"
496+
"limit": "17.08 kB"
497497
},
498498
{
499499
"name": "[Storage] downloadData (S3)",

0 commit comments

Comments
 (0)