Skip to content

Commit 87666a9

Browse files
authored
release(required): Amplify JS release (#14038)
2 parents 20798fb + 68c7f6f commit 87666a9

File tree

99 files changed

+5471
-283
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+5471
-283
lines changed

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,3 +929,64 @@ tests:
929929
browser: [chrome]
930930
env:
931931
NEXT_PUBLIC_BACKEND_CONFIG: mfa-setup
932+
- test_name: integ_next_passwordless_auto_sign_in
933+
desc: 'passwordless auto sign in with session'
934+
framework: next
935+
category: auth
936+
sample_name: [mfa]
937+
spec: passwordless/auto-sign-in
938+
# browser: *minimal_browser_list
939+
browser: [chrome]
940+
env:
941+
NEXT_PUBLIC_BACKEND_CONFIG: pwl-autosignin
942+
- test_name: integ_next_passwordless_first_factor_selection
943+
desc: 'passwordless sign in with first factor selection'
944+
framework: next
945+
category: auth
946+
sample_name: [mfa]
947+
spec: passwordless/first-factor-selection
948+
# browser: *minimal_browser_list
949+
browser: [chrome]
950+
env:
951+
NEXT_PUBLIC_BACKEND_CONFIG: pwl-ffselect
952+
- test_name: integ_next_passwordless_preferred_challenge
953+
desc: 'passwordless sign in with preferred challenge'
954+
framework: next
955+
category: auth
956+
sample_name: [mfa]
957+
spec: passwordless/preferred-challenge
958+
# browser: *minimal_browser_list
959+
browser: [chrome]
960+
env:
961+
NEXT_PUBLIC_BACKEND_CONFIG: pwl-prefchal
962+
- test_name: integ_next_passwordless_sign_up
963+
desc: 'passwordless sign up'
964+
framework: next
965+
category: auth
966+
sample_name: [mfa]
967+
spec: passwordless/sign-up
968+
# browser: *minimal_browser_list
969+
browser: [chrome]
970+
env:
971+
NEXT_PUBLIC_BACKEND_CONFIG: pwl-signup
972+
- test_name: integ_next_passwordless_misc
973+
desc: 'passwordless miscellaneous flows'
974+
framework: next
975+
category: auth
976+
sample_name: [mfa]
977+
spec: passwordless/miscellaneous
978+
# browser: *minimal_browser_list
979+
browser: [chrome]
980+
env:
981+
NEXT_PUBLIC_BACKEND_CONFIG: pwl-misc
982+
- test_name: integ_next_passwordless_webauthn
983+
desc: 'passwordless webauthn sign in and lifecycle management'
984+
framework: next
985+
category: auth
986+
sample_name: [mfa]
987+
spec: passwordless/webauthn
988+
# chrome only
989+
# https://chromedevtools.github.io/devtools-protocol/tot/WebAuthn/
990+
browser: [chrome]
991+
env:
992+
NEXT_PUBLIC_BACKEND_CONFIG: pwl-webauthn
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import { Amplify, fetchAuthSession } from '@aws-amplify/core';
2+
import { decodeJWT } from '@aws-amplify/core/internals/utils';
3+
4+
import {
5+
createCompleteWebAuthnRegistrationClient,
6+
createStartWebAuthnRegistrationClient,
7+
} from '../../../src/foundation/factories/serviceClients/cognitoIdentityProvider';
8+
import {
9+
PasskeyError,
10+
PasskeyErrorCode,
11+
} from '../../../src/client/utils/passkey/errors';
12+
import { associateWebAuthnCredential } from '../../../src/client/apis/associateWebAuthnCredential';
13+
import {
14+
passkeyCredentialCreateOptions,
15+
passkeyRegistrationResult,
16+
} from '../../mockData';
17+
import { serializePkcWithAttestationToJson } from '../../../src/client/utils/passkey/serde';
18+
import * as utils from '../../../src/client/utils';
19+
import { getIsPasskeySupported } from '../../../src/client/utils/passkey/getIsPasskeySupported';
20+
import { setUpGetConfig } from '../../providers/cognito/testUtils/setUpGetConfig';
21+
import { mockAccessToken } from '../../providers/cognito/testUtils/data';
22+
import {
23+
assertCredentialIsPkcWithAuthenticatorAssertionResponse,
24+
assertCredentialIsPkcWithAuthenticatorAttestationResponse,
25+
} from '../../../src/client/utils/passkey/types';
26+
27+
jest.mock('@aws-amplify/core', () => ({
28+
...(jest.createMockFromModule('@aws-amplify/core') as object),
29+
Amplify: { getConfig: jest.fn(() => ({})) },
30+
}));
31+
jest.mock('@aws-amplify/core/internals/utils', () => ({
32+
...jest.requireActual('@aws-amplify/core/internals/utils'),
33+
isBrowser: jest.fn(() => false),
34+
}));
35+
jest.mock(
36+
'../../../src/foundation/factories/serviceClients/cognitoIdentityProvider',
37+
);
38+
jest.mock('../../../src/providers/cognito/factories');
39+
40+
jest.mock('../../../src/client/utils/passkey/getIsPasskeySupported');
41+
jest.mock('../../../src/client/utils/passkey/types', () => ({
42+
...jest.requireActual('../../../src/client/utils/passkey/types'),
43+
assertCredentialIsPkcWithAuthenticatorAssertionResponse: jest.fn(),
44+
assertCredentialIsPkcWithAuthenticatorAttestationResponse: jest.fn(),
45+
}));
46+
47+
Object.assign(navigator, {
48+
credentials: {
49+
create: jest.fn(),
50+
},
51+
});
52+
53+
describe('associateWebAuthnCredential', () => {
54+
const navigatorCredentialsCreateSpy = jest.spyOn(
55+
navigator.credentials,
56+
'create',
57+
);
58+
const registerPasskeySpy = jest.spyOn(utils, 'registerPasskey');
59+
60+
const mockFetchAuthSession = jest.mocked(fetchAuthSession);
61+
62+
const mockGetIsPasskeySupported = jest.mocked(getIsPasskeySupported);
63+
64+
const mockStartWebAuthnRegistration = jest.fn();
65+
const mockCreateStartWebAuthnRegistrationClient = jest.mocked(
66+
createStartWebAuthnRegistrationClient,
67+
);
68+
69+
const mockCompleteWebAuthnRegistration = jest.fn();
70+
const mockCreateCompleteWebAuthnRegistrationClient = jest.mocked(
71+
createCompleteWebAuthnRegistrationClient,
72+
);
73+
74+
const mockAssertCredentialIsPkcWithAuthenticatorAssertionResponse =
75+
jest.mocked(assertCredentialIsPkcWithAuthenticatorAssertionResponse);
76+
const mockAssertCredentialIsPkcWithAuthenticatorAttestationResponse =
77+
jest.mocked(assertCredentialIsPkcWithAuthenticatorAttestationResponse);
78+
79+
beforeAll(() => {
80+
setUpGetConfig(Amplify);
81+
mockFetchAuthSession.mockResolvedValue({
82+
tokens: { accessToken: decodeJWT(mockAccessToken) },
83+
});
84+
mockCreateStartWebAuthnRegistrationClient.mockReturnValue(
85+
mockStartWebAuthnRegistration,
86+
);
87+
mockCreateCompleteWebAuthnRegistrationClient.mockReturnValue(
88+
mockCompleteWebAuthnRegistration,
89+
);
90+
mockCompleteWebAuthnRegistration.mockImplementation(() => ({
91+
CredentialId: '12345',
92+
}));
93+
94+
navigatorCredentialsCreateSpy.mockResolvedValue(passkeyRegistrationResult);
95+
96+
mockGetIsPasskeySupported.mockReturnValue(true);
97+
mockAssertCredentialIsPkcWithAuthenticatorAssertionResponse.mockImplementation(
98+
() => undefined,
99+
);
100+
mockAssertCredentialIsPkcWithAuthenticatorAttestationResponse.mockImplementation(
101+
() => undefined,
102+
);
103+
});
104+
105+
afterEach(() => {
106+
mockFetchAuthSession.mockClear();
107+
mockStartWebAuthnRegistration.mockClear();
108+
navigatorCredentialsCreateSpy.mockClear();
109+
});
110+
111+
it('should pass the correct service options when retrieving credential creation options', async () => {
112+
mockStartWebAuthnRegistration.mockImplementation(() => ({
113+
CredentialCreationOptions: passkeyCredentialCreateOptions,
114+
}));
115+
116+
await associateWebAuthnCredential();
117+
118+
expect(mockStartWebAuthnRegistration).toHaveBeenCalledWith(
119+
{
120+
region: 'us-west-2',
121+
userAgentValue: expect.any(String),
122+
},
123+
{
124+
AccessToken: mockAccessToken,
125+
},
126+
);
127+
});
128+
129+
it('should pass the correct service options when verifying a credential', async () => {
130+
mockStartWebAuthnRegistration.mockImplementation(() => ({
131+
CredentialCreationOptions: passkeyCredentialCreateOptions,
132+
}));
133+
134+
await associateWebAuthnCredential();
135+
136+
expect(mockCompleteWebAuthnRegistration).toHaveBeenCalledWith(
137+
{
138+
region: 'us-west-2',
139+
userAgentValue: expect.any(String),
140+
},
141+
{
142+
AccessToken: mockAccessToken,
143+
Credential: serializePkcWithAttestationToJson(
144+
passkeyRegistrationResult,
145+
),
146+
},
147+
);
148+
});
149+
150+
it('should call the registerPasskey function with correct input', async () => {
151+
mockStartWebAuthnRegistration.mockImplementation(() => ({
152+
CredentialCreationOptions: passkeyCredentialCreateOptions,
153+
}));
154+
155+
await associateWebAuthnCredential();
156+
157+
expect(registerPasskeySpy).toHaveBeenCalledWith(
158+
passkeyCredentialCreateOptions,
159+
);
160+
161+
expect(navigatorCredentialsCreateSpy).toHaveBeenCalled();
162+
});
163+
164+
it('should throw an error when service returns empty credential creation options', async () => {
165+
expect.assertions(2);
166+
167+
mockStartWebAuthnRegistration.mockImplementation(() => ({
168+
CredentialCreationOptions: undefined,
169+
}));
170+
171+
try {
172+
await associateWebAuthnCredential();
173+
} catch (error: any) {
174+
expect(error).toBeInstanceOf(PasskeyError);
175+
expect(error.name).toBe(
176+
PasskeyErrorCode.InvalidPasskeyRegistrationOptions,
177+
);
178+
}
179+
});
180+
181+
it('should throw an error when passkeys are not supported', async () => {
182+
expect.assertions(2);
183+
184+
mockStartWebAuthnRegistration.mockImplementation(() => ({
185+
CredentialCreationOptions: passkeyCredentialCreateOptions,
186+
}));
187+
188+
mockGetIsPasskeySupported.mockReturnValue(false);
189+
190+
try {
191+
await associateWebAuthnCredential();
192+
} catch (error: any) {
193+
expect(error).toBeInstanceOf(PasskeyError);
194+
expect(error.name).toBe(PasskeyErrorCode.PasskeyNotSupported);
195+
}
196+
});
197+
});

0 commit comments

Comments
 (0)