Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

release(required): Amplify JS release #14091

Merged
merged 9 commits into from
Dec 19, 2024
18 changes: 17 additions & 1 deletion .github/integ-config/integ-all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,23 @@ tests:
sample_name: [subdomains]
spec: subdomains
browser: [chrome]

- test_name: integ_next_custom_auth
desc: 'Sign-in with Custom Auth flow'
framework: next
category: auth
sample_name: [custom-auth]
spec: custom-auth
browser: *minimal_browser_list
- test_name: integ_next_auth_sign_in_with_sms_mfa
desc: 'Resumable sign in with SMS MFA flow'
framework: next
category: auth
sample_name: [mfa]
spec: sign-in-resumable-mfa
browser: *minimal_browser_list
env:
NEXT_PUBLIC_BACKEND_CONFIG: resum-signin

# DISABLED Angular/Vue tests:
# TODO: delete tests or add custom ui logic to support them.

Expand Down
13 changes: 5 additions & 8 deletions .github/workflows/callable-canary-sampleapp-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ jobs:
working-directory: amplify-js-samples-staging/samples/next/auth/new-next-app
- name: Copy files from samples staging repo
run: |
rm -r ./samples/next/auth/new-next-app
rm -r ./samples/next/auth/new-next-app
cp -r ./samples/next/auth/auth-rsc ./samples/next/auth/new-next-app
working-directory: amplify-js-samples-staging
- name: Copy test file from samples staging repo
Expand All @@ -124,10 +124,8 @@ jobs:
- name: Install dependencies
run: npm install
working-directory: amplify-js-samples-staging/samples/next/auth/new-next-app
- name: Install amplify
run: |
npm install -g npm@latest
npm install aws-amplify -legacy-peer-deps
- name: Install latest stable amplify version
run: npm install aws-amplify@latest @aws-amplify/adapter-nextjs@latest
working-directory: amplify-js-samples-staging/samples/next/auth/new-next-app
- name: Start application and run test
run: |
Expand Down Expand Up @@ -186,15 +184,14 @@ jobs:
working-directory: amplify-js-samples-staging/samples/javascript/datastore
- name: Install amplify
run: |
npm install -g npm@latest
npm install aws-amplify -legacy-peer-deps
npm install aws-amplify@latest
working-directory: amplify-js-samples-staging/samples/javascript/datastore/new-javascript-app
- name: Remove existing src folder
run: rm -rf src
working-directory: amplify-js-samples-staging/samples/javascript/datastore/new-javascript-app
- name: Copy files from samples staging repo
run: |
rm -r ./samples/javascript/datastore/new-javascript-app
rm -r ./samples/javascript/datastore/new-javascript-app
cp -r ./samples/javascript/datastore/basic-crud ./samples/javascript/datastore/new-javascript-app
working-directory: amplify-js-samples-staging
- name: Copy test file from samples staging repo
Expand Down
23 changes: 22 additions & 1 deletion packages/adapter-nextjs/__tests__/createServerRunner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ const mockAmplifyConfig: ResourcesConfig = {
},
},
};

jest.mock(
'../src/utils/createCookieStorageAdapterFromNextServerContext',
() => ({
Expand All @@ -30,6 +29,7 @@ jest.mock(

describe('createServerRunner', () => {
let createServerRunner: any;
let createRunWithAmplifyServerContextSpy: any;

const mockParseAmplifyConfig = jest.fn();
const mockCreateAWSCredentialsAndIdentityIdProvider = jest.fn();
Expand All @@ -50,11 +50,16 @@ describe('createServerRunner', () => {
jest.doMock('@aws-amplify/core/internals/utils', () => ({
parseAmplifyConfig: mockParseAmplifyConfig,
}));
createRunWithAmplifyServerContextSpy = jest.spyOn(
require('../src/utils/createRunWithAmplifyServerContext'),
'createRunWithAmplifyServerContext',
);

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

afterEach(() => {
createRunWithAmplifyServerContextSpy.mockClear();
mockParseAmplifyConfig.mockClear();
mockCreateAWSCredentialsAndIdentityIdProvider.mockClear();
mockCreateKeyValueStorageFromCookieStorageAdapter.mockClear();
Expand Down Expand Up @@ -98,6 +103,10 @@ describe('createServerRunner', () => {
{},
operation,
);
expect(createRunWithAmplifyServerContextSpy).toHaveBeenCalledWith({
config: mockAmplifyConfigWithoutAuth,
tokenValidator: undefined,
});
});
});

Expand All @@ -120,6 +129,12 @@ describe('createServerRunner', () => {
mockAmplifyConfig.Auth,
sharedInMemoryStorage,
);
expect(createRunWithAmplifyServerContextSpy).toHaveBeenCalledWith({
config: mockAmplifyConfig,
tokenValidator: expect.objectContaining({
getItem: expect.any(Function),
}),
});
});
});

Expand Down Expand Up @@ -162,6 +177,12 @@ describe('createServerRunner', () => {
mockAmplifyConfig.Auth,
mockCookieStorageAdapter,
);
expect(createRunWithAmplifyServerContextSpy).toHaveBeenCalledWith({
config: mockAmplifyConfig,
tokenValidator: expect.objectContaining({
getItem: expect.any(Function),
}),
});
});
});
});
Expand Down
151 changes: 87 additions & 64 deletions packages/adapter-nextjs/__tests__/utils/createTokenValidator.test.ts
Original file line number Diff line number Diff line change
@@ -1,85 +1,108 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { CognitoJwtVerifier } from 'aws-jwt-verify';

import { isValidCognitoToken } from '../../src/utils/isValidCognitoToken';
import { createTokenValidator } from '../../src/utils/createTokenValidator';
import { JwtVerifier } from '../../src/types';

jest.mock('aws-jwt-verify');
jest.mock('../../src/utils/isValidCognitoToken');

const mockIsValidCognitoToken = isValidCognitoToken as jest.Mock;

const userPoolId = 'userPoolId';
const userPoolClientId = 'clientId';
const tokenValidatorInput = {
userPoolId,
userPoolClientId,
};
const accessToken = {
key: 'CognitoIdentityServiceProvider.clientId.usersub.accessToken',
value:
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMTEiLCJpc3MiOiJodHRwc',
};
const idToken = {
key: 'CognitoIdentityServiceProvider.clientId.usersub.idToken',
value: 'eyJzdWIiOiIxMTEiLCJpc3MiOiJodHRwc.XAiOiJKV1QiLCJhbGciOiJIUzI1NiJ',
};

const tokenValidator = createTokenValidator({
userPoolId,
userPoolClientId,
});
describe('createTokenValidator', () => {
const userPoolId = 'userPoolId';
const userPoolClientId = 'clientId';
const accessToken = {
key: 'CognitoIdentityServiceProvider.clientId.usersub.accessToken',
value: 'access-token-value',
};
const idToken = {
key: 'CognitoIdentityServiceProvider.clientId.usersub.idToken',
value: 'id-token-value',
};

const mockIsValidCognitoToken = jest.mocked(isValidCognitoToken);
const mockCognitoJwtVerifier = {
create: jest.mocked(CognitoJwtVerifier.create),
};

describe('Validator', () => {
afterEach(() => {
jest.resetAllMocks();
});
it('should return a validator', () => {
expect(createTokenValidator(tokenValidatorInput)).toBeDefined();
mockIsValidCognitoToken.mockClear();
});

it('should return true for non-token keys', async () => {
const result = await tokenValidator.getItem?.('mockKey', 'mockValue');
expect(result).toBe(true);
expect(mockIsValidCognitoToken).toHaveBeenCalledTimes(0);
it('should return a token validator', () => {
expect(
createTokenValidator({
userPoolId,
userPoolClientId,
}),
).toStrictEqual({
getItem: expect.any(Function),
});
});

it('should return true for valid accessToken', async () => {
mockIsValidCognitoToken.mockImplementation(() => Promise.resolve(true));

const result = await tokenValidator.getItem?.(
accessToken.key,
accessToken.value,
);

expect(result).toBe(true);
expect(mockIsValidCognitoToken).toHaveBeenCalledTimes(1);
expect(mockIsValidCognitoToken).toHaveBeenCalledWith({
userPoolId,
clientId: userPoolClientId,
token: accessToken.value,
tokenType: 'access',
describe('created token validator', () => {
afterEach(() => {
mockCognitoJwtVerifier.create.mockReset();
});
});

it('should return true for valid idToken', async () => {
mockIsValidCognitoToken.mockImplementation(() => Promise.resolve(true));

const result = await tokenValidator.getItem?.(idToken.key, idToken.value);
expect(result).toBe(true);
expect(mockIsValidCognitoToken).toHaveBeenCalledTimes(1);
expect(mockIsValidCognitoToken).toHaveBeenCalledWith({
userPoolId,
clientId: userPoolClientId,
token: idToken.value,
tokenType: 'id',
it('should return true if key is not for access or id tokens', async () => {
const tokenValidator = createTokenValidator({
userPoolId,
userPoolClientId,
});

expect(await tokenValidator.getItem?.('key', 'value')).toBe(true);
expect(mockIsValidCognitoToken).not.toHaveBeenCalled();
});
});

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

const result = await tokenValidator.getItem?.(idToken.key, idToken.value);
expect(result).toBe(false);
expect(mockIsValidCognitoToken).toHaveBeenCalledTimes(1);
expect(
await tokenValidator.getItem?.(accessToken.key, accessToken.value),
).toBe(false);
expect(await tokenValidator.getItem?.(idToken.key, idToken.value)).toBe(
false,
);
expect(mockIsValidCognitoToken).not.toHaveBeenCalled();
});

describe.each([
{ tokenUse: 'access', token: accessToken },
{ tokenUse: 'id', token: idToken },
])('$tokenUse token verifier', ({ tokenUse, token }) => {
const mockTokenVerifier = {} as JwtVerifier;
const tokenValidator = createTokenValidator({
userPoolId,
userPoolClientId,
});

beforeAll(() => {
mockCognitoJwtVerifier.create.mockReturnValue(mockTokenVerifier);
});

it('should create a jwt verifier and use it to validate', async () => {
await tokenValidator.getItem?.(token.key, token.value);

expect(mockCognitoJwtVerifier.create).toHaveBeenCalledWith({
userPoolId,
clientId: userPoolClientId,
tokenUse,
});
expect(mockIsValidCognitoToken).toHaveBeenCalledWith({
token: token.value,
verifier: mockTokenVerifier,
});
});

it('should not re-create the jwt verifier', async () => {
await tokenValidator.getItem?.(token.key, token.value);

expect(mockCognitoJwtVerifier.create).not.toHaveBeenCalled();
expect(mockIsValidCognitoToken).toHaveBeenCalled();
});
});
});
});
Loading
Loading