Skip to content

Commit eb5cb51

Browse files
release(required): Amplify JS release (#14091)
2 parents 74894a0 + 29b6c3f commit eb5cb51

30 files changed

+786
-245
lines changed

.github/integ-config/integ-all.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,23 @@ tests:
609609
sample_name: [subdomains]
610610
spec: subdomains
611611
browser: [chrome]
612-
612+
- test_name: integ_next_custom_auth
613+
desc: 'Sign-in with Custom Auth flow'
614+
framework: next
615+
category: auth
616+
sample_name: [custom-auth]
617+
spec: custom-auth
618+
browser: *minimal_browser_list
619+
- test_name: integ_next_auth_sign_in_with_sms_mfa
620+
desc: 'Resumable sign in with SMS MFA flow'
621+
framework: next
622+
category: auth
623+
sample_name: [mfa]
624+
spec: sign-in-resumable-mfa
625+
browser: *minimal_browser_list
626+
env:
627+
NEXT_PUBLIC_BACKEND_CONFIG: resum-signin
628+
613629
# DISABLED Angular/Vue tests:
614630
# TODO: delete tests or add custom ui logic to support them.
615631

.github/workflows/callable-canary-sampleapp-tests.yml

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ jobs:
114114
working-directory: amplify-js-samples-staging/samples/next/auth/new-next-app
115115
- name: Copy files from samples staging repo
116116
run: |
117-
rm -r ./samples/next/auth/new-next-app
117+
rm -r ./samples/next/auth/new-next-app
118118
cp -r ./samples/next/auth/auth-rsc ./samples/next/auth/new-next-app
119119
working-directory: amplify-js-samples-staging
120120
- name: Copy test file from samples staging repo
@@ -124,10 +124,8 @@ jobs:
124124
- name: Install dependencies
125125
run: npm install
126126
working-directory: amplify-js-samples-staging/samples/next/auth/new-next-app
127-
- name: Install amplify
128-
run: |
129-
npm install -g npm@latest
130-
npm install aws-amplify -legacy-peer-deps
127+
- name: Install latest stable amplify version
128+
run: npm install aws-amplify@latest @aws-amplify/adapter-nextjs@latest
131129
working-directory: amplify-js-samples-staging/samples/next/auth/new-next-app
132130
- name: Start application and run test
133131
run: |
@@ -186,15 +184,14 @@ jobs:
186184
working-directory: amplify-js-samples-staging/samples/javascript/datastore
187185
- name: Install amplify
188186
run: |
189-
npm install -g npm@latest
190-
npm install aws-amplify -legacy-peer-deps
187+
npm install aws-amplify@latest
191188
working-directory: amplify-js-samples-staging/samples/javascript/datastore/new-javascript-app
192189
- name: Remove existing src folder
193190
run: rm -rf src
194191
working-directory: amplify-js-samples-staging/samples/javascript/datastore/new-javascript-app
195192
- name: Copy files from samples staging repo
196193
run: |
197-
rm -r ./samples/javascript/datastore/new-javascript-app
194+
rm -r ./samples/javascript/datastore/new-javascript-app
198195
cp -r ./samples/javascript/datastore/basic-crud ./samples/javascript/datastore/new-javascript-app
199196
working-directory: amplify-js-samples-staging
200197
- name: Copy test file from samples staging repo

packages/adapter-nextjs/__tests__/createServerRunner.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ const mockAmplifyConfig: ResourcesConfig = {
2020
},
2121
},
2222
};
23-
2423
jest.mock(
2524
'../src/utils/createCookieStorageAdapterFromNextServerContext',
2625
() => ({
@@ -30,6 +29,7 @@ jest.mock(
3029

3130
describe('createServerRunner', () => {
3231
let createServerRunner: any;
32+
let createRunWithAmplifyServerContextSpy: any;
3333

3434
const mockParseAmplifyConfig = jest.fn();
3535
const mockCreateAWSCredentialsAndIdentityIdProvider = jest.fn();
@@ -50,11 +50,16 @@ describe('createServerRunner', () => {
5050
jest.doMock('@aws-amplify/core/internals/utils', () => ({
5151
parseAmplifyConfig: mockParseAmplifyConfig,
5252
}));
53+
createRunWithAmplifyServerContextSpy = jest.spyOn(
54+
require('../src/utils/createRunWithAmplifyServerContext'),
55+
'createRunWithAmplifyServerContext',
56+
);
5357

5458
({ createServerRunner } = require('../src'));
5559
});
5660

5761
afterEach(() => {
62+
createRunWithAmplifyServerContextSpy.mockClear();
5863
mockParseAmplifyConfig.mockClear();
5964
mockCreateAWSCredentialsAndIdentityIdProvider.mockClear();
6065
mockCreateKeyValueStorageFromCookieStorageAdapter.mockClear();
@@ -98,6 +103,10 @@ describe('createServerRunner', () => {
98103
{},
99104
operation,
100105
);
106+
expect(createRunWithAmplifyServerContextSpy).toHaveBeenCalledWith({
107+
config: mockAmplifyConfigWithoutAuth,
108+
tokenValidator: undefined,
109+
});
101110
});
102111
});
103112

@@ -120,6 +129,12 @@ describe('createServerRunner', () => {
120129
mockAmplifyConfig.Auth,
121130
sharedInMemoryStorage,
122131
);
132+
expect(createRunWithAmplifyServerContextSpy).toHaveBeenCalledWith({
133+
config: mockAmplifyConfig,
134+
tokenValidator: expect.objectContaining({
135+
getItem: expect.any(Function),
136+
}),
137+
});
123138
});
124139
});
125140

@@ -162,6 +177,12 @@ describe('createServerRunner', () => {
162177
mockAmplifyConfig.Auth,
163178
mockCookieStorageAdapter,
164179
);
180+
expect(createRunWithAmplifyServerContextSpy).toHaveBeenCalledWith({
181+
config: mockAmplifyConfig,
182+
tokenValidator: expect.objectContaining({
183+
getItem: expect.any(Function),
184+
}),
185+
});
165186
});
166187
});
167188
});
Lines changed: 87 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,108 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4+
import { CognitoJwtVerifier } from 'aws-jwt-verify';
5+
46
import { isValidCognitoToken } from '../../src/utils/isValidCognitoToken';
57
import { createTokenValidator } from '../../src/utils/createTokenValidator';
8+
import { JwtVerifier } from '../../src/types';
69

10+
jest.mock('aws-jwt-verify');
711
jest.mock('../../src/utils/isValidCognitoToken');
812

9-
const mockIsValidCognitoToken = isValidCognitoToken as jest.Mock;
10-
11-
const userPoolId = 'userPoolId';
12-
const userPoolClientId = 'clientId';
13-
const tokenValidatorInput = {
14-
userPoolId,
15-
userPoolClientId,
16-
};
17-
const accessToken = {
18-
key: 'CognitoIdentityServiceProvider.clientId.usersub.accessToken',
19-
value:
20-
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMTEiLCJpc3MiOiJodHRwc',
21-
};
22-
const idToken = {
23-
key: 'CognitoIdentityServiceProvider.clientId.usersub.idToken',
24-
value: 'eyJzdWIiOiIxMTEiLCJpc3MiOiJodHRwc.XAiOiJKV1QiLCJhbGciOiJIUzI1NiJ',
25-
};
26-
27-
const tokenValidator = createTokenValidator({
28-
userPoolId,
29-
userPoolClientId,
30-
});
13+
describe('createTokenValidator', () => {
14+
const userPoolId = 'userPoolId';
15+
const userPoolClientId = 'clientId';
16+
const accessToken = {
17+
key: 'CognitoIdentityServiceProvider.clientId.usersub.accessToken',
18+
value: 'access-token-value',
19+
};
20+
const idToken = {
21+
key: 'CognitoIdentityServiceProvider.clientId.usersub.idToken',
22+
value: 'id-token-value',
23+
};
24+
25+
const mockIsValidCognitoToken = jest.mocked(isValidCognitoToken);
26+
const mockCognitoJwtVerifier = {
27+
create: jest.mocked(CognitoJwtVerifier.create),
28+
};
3129

32-
describe('Validator', () => {
3330
afterEach(() => {
34-
jest.resetAllMocks();
35-
});
36-
it('should return a validator', () => {
37-
expect(createTokenValidator(tokenValidatorInput)).toBeDefined();
31+
mockIsValidCognitoToken.mockClear();
3832
});
3933

40-
it('should return true for non-token keys', async () => {
41-
const result = await tokenValidator.getItem?.('mockKey', 'mockValue');
42-
expect(result).toBe(true);
43-
expect(mockIsValidCognitoToken).toHaveBeenCalledTimes(0);
34+
it('should return a token validator', () => {
35+
expect(
36+
createTokenValidator({
37+
userPoolId,
38+
userPoolClientId,
39+
}),
40+
).toStrictEqual({
41+
getItem: expect.any(Function),
42+
});
4443
});
4544

46-
it('should return true for valid accessToken', async () => {
47-
mockIsValidCognitoToken.mockImplementation(() => Promise.resolve(true));
48-
49-
const result = await tokenValidator.getItem?.(
50-
accessToken.key,
51-
accessToken.value,
52-
);
53-
54-
expect(result).toBe(true);
55-
expect(mockIsValidCognitoToken).toHaveBeenCalledTimes(1);
56-
expect(mockIsValidCognitoToken).toHaveBeenCalledWith({
57-
userPoolId,
58-
clientId: userPoolClientId,
59-
token: accessToken.value,
60-
tokenType: 'access',
45+
describe('created token validator', () => {
46+
afterEach(() => {
47+
mockCognitoJwtVerifier.create.mockReset();
6148
});
62-
});
6349

64-
it('should return true for valid idToken', async () => {
65-
mockIsValidCognitoToken.mockImplementation(() => Promise.resolve(true));
66-
67-
const result = await tokenValidator.getItem?.(idToken.key, idToken.value);
68-
expect(result).toBe(true);
69-
expect(mockIsValidCognitoToken).toHaveBeenCalledTimes(1);
70-
expect(mockIsValidCognitoToken).toHaveBeenCalledWith({
71-
userPoolId,
72-
clientId: userPoolClientId,
73-
token: idToken.value,
74-
tokenType: 'id',
50+
it('should return true if key is not for access or id tokens', async () => {
51+
const tokenValidator = createTokenValidator({
52+
userPoolId,
53+
userPoolClientId,
54+
});
55+
56+
expect(await tokenValidator.getItem?.('key', 'value')).toBe(true);
57+
expect(mockIsValidCognitoToken).not.toHaveBeenCalled();
7558
});
76-
});
7759

78-
it('should return false if invalid tokenType is access', async () => {
79-
mockIsValidCognitoToken.mockImplementation(() => Promise.resolve(false));
60+
it('should return false if validator created without user pool or client ids', async () => {
61+
const tokenValidator = createTokenValidator({});
8062

81-
const result = await tokenValidator.getItem?.(idToken.key, idToken.value);
82-
expect(result).toBe(false);
83-
expect(mockIsValidCognitoToken).toHaveBeenCalledTimes(1);
63+
expect(
64+
await tokenValidator.getItem?.(accessToken.key, accessToken.value),
65+
).toBe(false);
66+
expect(await tokenValidator.getItem?.(idToken.key, idToken.value)).toBe(
67+
false,
68+
);
69+
expect(mockIsValidCognitoToken).not.toHaveBeenCalled();
70+
});
71+
72+
describe.each([
73+
{ tokenUse: 'access', token: accessToken },
74+
{ tokenUse: 'id', token: idToken },
75+
])('$tokenUse token verifier', ({ tokenUse, token }) => {
76+
const mockTokenVerifier = {} as JwtVerifier;
77+
const tokenValidator = createTokenValidator({
78+
userPoolId,
79+
userPoolClientId,
80+
});
81+
82+
beforeAll(() => {
83+
mockCognitoJwtVerifier.create.mockReturnValue(mockTokenVerifier);
84+
});
85+
86+
it('should create a jwt verifier and use it to validate', async () => {
87+
await tokenValidator.getItem?.(token.key, token.value);
88+
89+
expect(mockCognitoJwtVerifier.create).toHaveBeenCalledWith({
90+
userPoolId,
91+
clientId: userPoolClientId,
92+
tokenUse,
93+
});
94+
expect(mockIsValidCognitoToken).toHaveBeenCalledWith({
95+
token: token.value,
96+
verifier: mockTokenVerifier,
97+
});
98+
});
99+
100+
it('should not re-create the jwt verifier', async () => {
101+
await tokenValidator.getItem?.(token.key, token.value);
102+
103+
expect(mockCognitoJwtVerifier.create).not.toHaveBeenCalled();
104+
expect(mockIsValidCognitoToken).toHaveBeenCalled();
105+
});
106+
});
84107
});
85108
});

0 commit comments

Comments
 (0)