Skip to content

Commit

Permalink
Merge branch 'main' into feat/next-15/main
Browse files Browse the repository at this point in the history
  • Loading branch information
HuiSF authored Nov 26, 2024
2 parents da0294a + 68c7f6f commit 138b90a
Show file tree
Hide file tree
Showing 99 changed files with 5,471 additions and 283 deletions.
61 changes: 61 additions & 0 deletions .github/integ-config/integ-all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -929,3 +929,64 @@ tests:
browser: [chrome]
env:
NEXT_PUBLIC_BACKEND_CONFIG: mfa-setup
- test_name: integ_next_passwordless_auto_sign_in
desc: 'passwordless auto sign in with session'
framework: next
category: auth
sample_name: [mfa]
spec: passwordless/auto-sign-in
# browser: *minimal_browser_list
browser: [chrome]
env:
NEXT_PUBLIC_BACKEND_CONFIG: pwl-autosignin
- test_name: integ_next_passwordless_first_factor_selection
desc: 'passwordless sign in with first factor selection'
framework: next
category: auth
sample_name: [mfa]
spec: passwordless/first-factor-selection
# browser: *minimal_browser_list
browser: [chrome]
env:
NEXT_PUBLIC_BACKEND_CONFIG: pwl-ffselect
- test_name: integ_next_passwordless_preferred_challenge
desc: 'passwordless sign in with preferred challenge'
framework: next
category: auth
sample_name: [mfa]
spec: passwordless/preferred-challenge
# browser: *minimal_browser_list
browser: [chrome]
env:
NEXT_PUBLIC_BACKEND_CONFIG: pwl-prefchal
- test_name: integ_next_passwordless_sign_up
desc: 'passwordless sign up'
framework: next
category: auth
sample_name: [mfa]
spec: passwordless/sign-up
# browser: *minimal_browser_list
browser: [chrome]
env:
NEXT_PUBLIC_BACKEND_CONFIG: pwl-signup
- test_name: integ_next_passwordless_misc
desc: 'passwordless miscellaneous flows'
framework: next
category: auth
sample_name: [mfa]
spec: passwordless/miscellaneous
# browser: *minimal_browser_list
browser: [chrome]
env:
NEXT_PUBLIC_BACKEND_CONFIG: pwl-misc
- test_name: integ_next_passwordless_webauthn
desc: 'passwordless webauthn sign in and lifecycle management'
framework: next
category: auth
sample_name: [mfa]
spec: passwordless/webauthn
# chrome only
# https://chromedevtools.github.io/devtools-protocol/tot/WebAuthn/
browser: [chrome]
env:
NEXT_PUBLIC_BACKEND_CONFIG: pwl-webauthn
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import { Amplify, fetchAuthSession } from '@aws-amplify/core';
import { decodeJWT } from '@aws-amplify/core/internals/utils';

import {
createCompleteWebAuthnRegistrationClient,
createStartWebAuthnRegistrationClient,
} from '../../../src/foundation/factories/serviceClients/cognitoIdentityProvider';
import {
PasskeyError,
PasskeyErrorCode,
} from '../../../src/client/utils/passkey/errors';
import { associateWebAuthnCredential } from '../../../src/client/apis/associateWebAuthnCredential';
import {
passkeyCredentialCreateOptions,
passkeyRegistrationResult,
} from '../../mockData';
import { serializePkcWithAttestationToJson } from '../../../src/client/utils/passkey/serde';
import * as utils from '../../../src/client/utils';
import { getIsPasskeySupported } from '../../../src/client/utils/passkey/getIsPasskeySupported';
import { setUpGetConfig } from '../../providers/cognito/testUtils/setUpGetConfig';
import { mockAccessToken } from '../../providers/cognito/testUtils/data';
import {
assertCredentialIsPkcWithAuthenticatorAssertionResponse,
assertCredentialIsPkcWithAuthenticatorAttestationResponse,
} from '../../../src/client/utils/passkey/types';

jest.mock('@aws-amplify/core', () => ({
...(jest.createMockFromModule('@aws-amplify/core') as object),
Amplify: { getConfig: jest.fn(() => ({})) },
}));
jest.mock('@aws-amplify/core/internals/utils', () => ({
...jest.requireActual('@aws-amplify/core/internals/utils'),
isBrowser: jest.fn(() => false),
}));
jest.mock(
'../../../src/foundation/factories/serviceClients/cognitoIdentityProvider',
);
jest.mock('../../../src/providers/cognito/factories');

jest.mock('../../../src/client/utils/passkey/getIsPasskeySupported');
jest.mock('../../../src/client/utils/passkey/types', () => ({
...jest.requireActual('../../../src/client/utils/passkey/types'),
assertCredentialIsPkcWithAuthenticatorAssertionResponse: jest.fn(),
assertCredentialIsPkcWithAuthenticatorAttestationResponse: jest.fn(),
}));

Object.assign(navigator, {
credentials: {
create: jest.fn(),
},
});

describe('associateWebAuthnCredential', () => {
const navigatorCredentialsCreateSpy = jest.spyOn(
navigator.credentials,
'create',
);
const registerPasskeySpy = jest.spyOn(utils, 'registerPasskey');

const mockFetchAuthSession = jest.mocked(fetchAuthSession);

const mockGetIsPasskeySupported = jest.mocked(getIsPasskeySupported);

const mockStartWebAuthnRegistration = jest.fn();
const mockCreateStartWebAuthnRegistrationClient = jest.mocked(
createStartWebAuthnRegistrationClient,
);

const mockCompleteWebAuthnRegistration = jest.fn();
const mockCreateCompleteWebAuthnRegistrationClient = jest.mocked(
createCompleteWebAuthnRegistrationClient,
);

const mockAssertCredentialIsPkcWithAuthenticatorAssertionResponse =
jest.mocked(assertCredentialIsPkcWithAuthenticatorAssertionResponse);
const mockAssertCredentialIsPkcWithAuthenticatorAttestationResponse =
jest.mocked(assertCredentialIsPkcWithAuthenticatorAttestationResponse);

beforeAll(() => {
setUpGetConfig(Amplify);
mockFetchAuthSession.mockResolvedValue({
tokens: { accessToken: decodeJWT(mockAccessToken) },
});
mockCreateStartWebAuthnRegistrationClient.mockReturnValue(
mockStartWebAuthnRegistration,
);
mockCreateCompleteWebAuthnRegistrationClient.mockReturnValue(
mockCompleteWebAuthnRegistration,
);
mockCompleteWebAuthnRegistration.mockImplementation(() => ({
CredentialId: '12345',
}));

navigatorCredentialsCreateSpy.mockResolvedValue(passkeyRegistrationResult);

mockGetIsPasskeySupported.mockReturnValue(true);
mockAssertCredentialIsPkcWithAuthenticatorAssertionResponse.mockImplementation(
() => undefined,
);
mockAssertCredentialIsPkcWithAuthenticatorAttestationResponse.mockImplementation(
() => undefined,
);
});

afterEach(() => {
mockFetchAuthSession.mockClear();
mockStartWebAuthnRegistration.mockClear();
navigatorCredentialsCreateSpy.mockClear();
});

it('should pass the correct service options when retrieving credential creation options', async () => {
mockStartWebAuthnRegistration.mockImplementation(() => ({
CredentialCreationOptions: passkeyCredentialCreateOptions,
}));

await associateWebAuthnCredential();

expect(mockStartWebAuthnRegistration).toHaveBeenCalledWith(
{
region: 'us-west-2',
userAgentValue: expect.any(String),
},
{
AccessToken: mockAccessToken,
},
);
});

it('should pass the correct service options when verifying a credential', async () => {
mockStartWebAuthnRegistration.mockImplementation(() => ({
CredentialCreationOptions: passkeyCredentialCreateOptions,
}));

await associateWebAuthnCredential();

expect(mockCompleteWebAuthnRegistration).toHaveBeenCalledWith(
{
region: 'us-west-2',
userAgentValue: expect.any(String),
},
{
AccessToken: mockAccessToken,
Credential: serializePkcWithAttestationToJson(
passkeyRegistrationResult,
),
},
);
});

it('should call the registerPasskey function with correct input', async () => {
mockStartWebAuthnRegistration.mockImplementation(() => ({
CredentialCreationOptions: passkeyCredentialCreateOptions,
}));

await associateWebAuthnCredential();

expect(registerPasskeySpy).toHaveBeenCalledWith(
passkeyCredentialCreateOptions,
);

expect(navigatorCredentialsCreateSpy).toHaveBeenCalled();
});

it('should throw an error when service returns empty credential creation options', async () => {
expect.assertions(2);

mockStartWebAuthnRegistration.mockImplementation(() => ({
CredentialCreationOptions: undefined,
}));

try {
await associateWebAuthnCredential();
} catch (error: any) {
expect(error).toBeInstanceOf(PasskeyError);
expect(error.name).toBe(
PasskeyErrorCode.InvalidPasskeyRegistrationOptions,
);
}
});

it('should throw an error when passkeys are not supported', async () => {
expect.assertions(2);

mockStartWebAuthnRegistration.mockImplementation(() => ({
CredentialCreationOptions: passkeyCredentialCreateOptions,
}));

mockGetIsPasskeySupported.mockReturnValue(false);

try {
await associateWebAuthnCredential();
} catch (error: any) {
expect(error).toBeInstanceOf(PasskeyError);
expect(error.name).toBe(PasskeyErrorCode.PasskeyNotSupported);
}
});
});
Loading

0 comments on commit 138b90a

Please sign in to comment.