@aws-amplify/ui 6.13.0 → 6.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/helpers/authenticator/facade.mjs +9 -2
- package/dist/esm/helpers/authenticator/formFields/defaults.mjs +34 -8
- package/dist/esm/helpers/authenticator/getRoute.mjs +8 -0
- package/dist/esm/helpers/authenticator/textUtil.mjs +34 -0
- package/dist/esm/i18n/dictionaries/authenticator/defaultTexts.mjs +27 -0
- package/dist/esm/i18n/dictionaries/authenticator/es.mjs +1 -1
- package/dist/esm/i18n/dictionaries/authenticator/fr.mjs +1 -1
- package/dist/esm/machines/authenticator/actions.mjs +63 -4
- package/dist/esm/machines/authenticator/actors/signIn.mjs +199 -49
- package/dist/esm/machines/authenticator/actors/signUp.mjs +81 -27
- package/dist/esm/machines/authenticator/defaultServices.mjs +37 -0
- package/dist/esm/machines/authenticator/guards.mjs +49 -1
- package/dist/esm/machines/authenticator/index.mjs +29 -15
- package/dist/esm/machines/authenticator/utils.mjs +58 -6
- package/dist/index.js +625 -111
- package/dist/styles/authenticator.css +17 -0
- package/dist/styles/authenticator.layer.css +17 -0
- package/dist/styles.css +17 -0
- package/dist/styles.layer.css +17 -0
- package/dist/types/helpers/authenticator/facade.d.ts +6 -2
- package/dist/types/helpers/authenticator/textUtil.d.ts +24 -1
- package/dist/types/i18n/dictionaries/authenticator/defaultTexts.d.ts +27 -0
- package/dist/types/i18n/dictionaries/index.d.ts +27 -0
- package/dist/types/i18n/translations.d.ts +27 -0
- package/dist/types/machines/authenticator/defaultServices.d.ts +105 -0
- package/dist/types/machines/authenticator/types.d.ts +26 -2
- package/dist/types/machines/authenticator/utils.d.ts +14 -2
- package/package.json +5 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createMachine, sendUpdate } from 'xstate';
|
|
2
|
-
import { signInWithRedirect, confirmSignIn, resetPassword, fetchUserAttributes } from 'aws-amplify/auth';
|
|
2
|
+
import { listWebAuthnCredentials, signInWithRedirect, confirmSignIn, resetPassword, fetchUserAttributes } from 'aws-amplify/auth';
|
|
3
3
|
import { runValidators } from '../../../validators/index.mjs';
|
|
4
4
|
import ACTIONS from '../actions.mjs';
|
|
5
5
|
import { defaultServices } from '../defaultServices.mjs';
|
|
@@ -35,32 +35,13 @@ const handleSignInResponse = {
|
|
|
35
35
|
'setNextSignInStep',
|
|
36
36
|
'setTotpSecretCode',
|
|
37
37
|
'setAllowedMfaTypes',
|
|
38
|
+
'setCodeDeliveryDetails',
|
|
38
39
|
],
|
|
39
40
|
target: '#signInActor.init',
|
|
40
41
|
},
|
|
41
42
|
],
|
|
42
43
|
onError: { actions: 'setRemoteError', target: 'edit' },
|
|
43
44
|
};
|
|
44
|
-
const handleFetchUserAttributesResponse = {
|
|
45
|
-
onDone: [
|
|
46
|
-
{
|
|
47
|
-
cond: 'shouldVerifyAttribute',
|
|
48
|
-
actions: [
|
|
49
|
-
'setShouldVerifyUserAttributeStep',
|
|
50
|
-
'setUnverifiedUserAttributes',
|
|
51
|
-
],
|
|
52
|
-
target: '#signInActor.resolved',
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
actions: 'setConfirmAttributeCompleteStep',
|
|
56
|
-
target: '#signInActor.resolved',
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
onError: {
|
|
60
|
-
actions: 'setConfirmAttributeCompleteStep',
|
|
61
|
-
target: '#signInActor.resolved',
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
45
|
const getDefaultConfirmSignInState = (exit) => ({
|
|
65
46
|
initial: 'edit',
|
|
66
47
|
exit,
|
|
@@ -71,6 +52,7 @@ const getDefaultConfirmSignInState = (exit) => ({
|
|
|
71
52
|
SUBMIT: { actions: 'handleSubmit', target: 'submit' },
|
|
72
53
|
SIGN_IN: '#signInActor.signIn',
|
|
73
54
|
CHANGE: { actions: 'handleInput' },
|
|
55
|
+
RESEND: { target: 'resend' },
|
|
74
56
|
},
|
|
75
57
|
},
|
|
76
58
|
submit: {
|
|
@@ -78,6 +60,21 @@ const getDefaultConfirmSignInState = (exit) => ({
|
|
|
78
60
|
entry: ['sendUpdate', 'clearError'],
|
|
79
61
|
invoke: { src: 'confirmSignIn', ...handleSignInResponse },
|
|
80
62
|
},
|
|
63
|
+
resend: {
|
|
64
|
+
tags: 'pending',
|
|
65
|
+
entry: ['sendUpdate', 'clearError'],
|
|
66
|
+
invoke: {
|
|
67
|
+
src: 'resendSignInCode',
|
|
68
|
+
onDone: {
|
|
69
|
+
actions: ['setCodeDeliveryDetails', 'sendUpdate'],
|
|
70
|
+
target: 'edit',
|
|
71
|
+
},
|
|
72
|
+
onError: {
|
|
73
|
+
actions: 'setRemoteError',
|
|
74
|
+
target: 'edit',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
81
78
|
},
|
|
82
79
|
});
|
|
83
80
|
function signInActor({ services }) {
|
|
@@ -112,13 +109,68 @@ function signInActor({ services }) {
|
|
|
112
109
|
{ target: 'signIn' },
|
|
113
110
|
],
|
|
114
111
|
},
|
|
115
|
-
federatedSignIn: getFederatedSignInState('signIn'),
|
|
112
|
+
federatedSignIn: { ...getFederatedSignInState('signIn') },
|
|
116
113
|
fetchUserAttributes: {
|
|
117
114
|
invoke: {
|
|
118
115
|
src: 'fetchUserAttributes',
|
|
119
|
-
|
|
116
|
+
onDone: [
|
|
117
|
+
{
|
|
118
|
+
cond: 'hasPasskeyRegistrationPrompts',
|
|
119
|
+
actions: 'setFetchedUserAttributes',
|
|
120
|
+
target: 'checkPasskeys',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
actions: 'setFetchedUserAttributes',
|
|
124
|
+
target: 'evaluatePasskeyPrompt',
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
onError: {
|
|
128
|
+
actions: 'setConfirmAttributeCompleteStep',
|
|
129
|
+
target: '#signInActor.resolved',
|
|
130
|
+
},
|
|
120
131
|
},
|
|
121
132
|
},
|
|
133
|
+
checkPasskeys: {
|
|
134
|
+
invoke: {
|
|
135
|
+
src: async () => {
|
|
136
|
+
try {
|
|
137
|
+
const result = await listWebAuthnCredentials();
|
|
138
|
+
return result.credentials && result.credentials.length > 0;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
onDone: {
|
|
145
|
+
actions: 'setHasExistingPasskeys',
|
|
146
|
+
target: 'evaluatePasskeyPrompt',
|
|
147
|
+
},
|
|
148
|
+
onError: {
|
|
149
|
+
actions: 'clearHasExistingPasskeys',
|
|
150
|
+
target: 'evaluatePasskeyPrompt',
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
evaluatePasskeyPrompt: {
|
|
155
|
+
always: [
|
|
156
|
+
{
|
|
157
|
+
cond: 'shouldPromptPasskeyRegistration',
|
|
158
|
+
target: '#signInActor.passkeyPrompt',
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
cond: 'shouldVerifyAttribute',
|
|
162
|
+
actions: [
|
|
163
|
+
'setShouldVerifyUserAttributeStep',
|
|
164
|
+
'setUnverifiedUserAttributes',
|
|
165
|
+
],
|
|
166
|
+
target: '#signInActor.resolved',
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
actions: 'setConfirmAttributeCompleteStep',
|
|
170
|
+
target: '#signInActor.resolved',
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
},
|
|
122
174
|
resendSignUpCode: {
|
|
123
175
|
invoke: {
|
|
124
176
|
src: 'handleResendSignUpCode',
|
|
@@ -153,23 +205,70 @@ function signInActor({ services }) {
|
|
|
153
205
|
on: {
|
|
154
206
|
CHANGE: { actions: 'handleInput' },
|
|
155
207
|
FEDERATED_SIGN_IN: { target: '#signInActor.federatedSignIn' },
|
|
156
|
-
|
|
208
|
+
SHOW_AUTH_METHODS: {
|
|
209
|
+
actions: 'setUsernameSignIn',
|
|
210
|
+
target: 'selectMethod',
|
|
211
|
+
},
|
|
212
|
+
SUBMIT: [
|
|
213
|
+
{
|
|
214
|
+
cond: 'shouldSelectAuthMethod',
|
|
215
|
+
actions: 'handleSubmit',
|
|
216
|
+
target: 'selectMethod',
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
actions: 'handleSubmit',
|
|
220
|
+
target: 'submit',
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
selectMethod: {
|
|
226
|
+
entry: [
|
|
227
|
+
'sendUpdate',
|
|
228
|
+
'setSelectAuthMethodStep',
|
|
229
|
+
'setUsernameSignIn',
|
|
230
|
+
],
|
|
231
|
+
on: {
|
|
232
|
+
SELECT_METHOD: {
|
|
233
|
+
actions: 'setSelectedAuthMethod',
|
|
234
|
+
target: 'submit',
|
|
235
|
+
},
|
|
236
|
+
SUBMIT: {
|
|
237
|
+
actions: ['handleSubmit', 'setSelectedAuthMethodFromForm'],
|
|
238
|
+
target: 'submit',
|
|
239
|
+
},
|
|
240
|
+
SIGN_IN: {
|
|
241
|
+
target: 'edit',
|
|
242
|
+
},
|
|
157
243
|
},
|
|
158
244
|
},
|
|
159
245
|
submit: {
|
|
160
246
|
tags: 'pending',
|
|
161
247
|
entry: ['clearError', 'sendUpdate', 'setUsernameSignIn'],
|
|
162
248
|
exit: 'clearFormValues',
|
|
163
|
-
invoke: {
|
|
249
|
+
invoke: {
|
|
250
|
+
src: 'handleSignIn',
|
|
251
|
+
onDone: handleSignInResponse.onDone,
|
|
252
|
+
onError: [
|
|
253
|
+
{
|
|
254
|
+
cond: 'shouldReturnToSelectMethod',
|
|
255
|
+
actions: 'setRemoteError',
|
|
256
|
+
target: 'selectMethod',
|
|
257
|
+
},
|
|
258
|
+
handleSignInResponse.onError,
|
|
259
|
+
],
|
|
260
|
+
},
|
|
164
261
|
},
|
|
165
262
|
},
|
|
166
263
|
},
|
|
167
|
-
confirmSignIn:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
264
|
+
confirmSignIn: {
|
|
265
|
+
...getDefaultConfirmSignInState([
|
|
266
|
+
'clearChallengeName',
|
|
267
|
+
'clearFormValues',
|
|
268
|
+
'clearError',
|
|
269
|
+
'clearTouched',
|
|
270
|
+
]),
|
|
271
|
+
},
|
|
173
272
|
forceChangePassword: {
|
|
174
273
|
entry: 'sendUpdate',
|
|
175
274
|
type: 'parallel',
|
|
@@ -242,21 +341,40 @@ function signInActor({ services }) {
|
|
|
242
341
|
},
|
|
243
342
|
},
|
|
244
343
|
},
|
|
245
|
-
setupTotp:
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
344
|
+
setupTotp: {
|
|
345
|
+
...getDefaultConfirmSignInState([
|
|
346
|
+
'clearFormValues',
|
|
347
|
+
'clearError',
|
|
348
|
+
'clearTouched',
|
|
349
|
+
]),
|
|
350
|
+
},
|
|
351
|
+
setupEmail: {
|
|
352
|
+
...getDefaultConfirmSignInState([
|
|
353
|
+
'clearFormValues',
|
|
354
|
+
'clearError',
|
|
355
|
+
'clearTouched',
|
|
356
|
+
]),
|
|
357
|
+
},
|
|
358
|
+
selectMfaType: {
|
|
359
|
+
...getDefaultConfirmSignInState([
|
|
360
|
+
'clearFormValues',
|
|
361
|
+
'clearError',
|
|
362
|
+
'clearTouched',
|
|
363
|
+
]),
|
|
364
|
+
},
|
|
365
|
+
passkeyPrompt: {
|
|
366
|
+
entry: 'sendUpdate',
|
|
367
|
+
on: {
|
|
368
|
+
SUBMIT: {
|
|
369
|
+
actions: 'setConfirmAttributeCompleteStep',
|
|
370
|
+
target: 'resolved',
|
|
371
|
+
},
|
|
372
|
+
SKIP: {
|
|
373
|
+
actions: 'setConfirmAttributeCompleteStep',
|
|
374
|
+
target: 'resolved',
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
},
|
|
260
378
|
resolved: {
|
|
261
379
|
type: 'final',
|
|
262
380
|
data: (context) => ({
|
|
@@ -282,9 +400,41 @@ function signInActor({ services }) {
|
|
|
282
400
|
handleResendSignUpCode({ username }) {
|
|
283
401
|
return services.handleResendSignUpCode({ username });
|
|
284
402
|
},
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
403
|
+
resendSignInCode({ username, selectedAuthMethod, availableAuthMethods, preferredChallenge, }) {
|
|
404
|
+
// Resend code by calling signIn again with the same parameters
|
|
405
|
+
const method = selectedAuthMethod ??
|
|
406
|
+
preferredChallenge ??
|
|
407
|
+
availableAuthMethods?.[0] ??
|
|
408
|
+
'PASSWORD';
|
|
409
|
+
return services.handleSignIn({
|
|
410
|
+
username,
|
|
411
|
+
options: {
|
|
412
|
+
authFlowType: 'USER_AUTH',
|
|
413
|
+
preferredChallenge: method,
|
|
414
|
+
},
|
|
415
|
+
});
|
|
416
|
+
},
|
|
417
|
+
handleSignIn({ formValues, username, selectedAuthMethod, availableAuthMethods, preferredChallenge, }) {
|
|
418
|
+
// Determine which method to use
|
|
419
|
+
const method = selectedAuthMethod ??
|
|
420
|
+
preferredChallenge ??
|
|
421
|
+
availableAuthMethods?.[0] ??
|
|
422
|
+
'PASSWORD';
|
|
423
|
+
if (method === 'PASSWORD') {
|
|
424
|
+
// Traditional password flow
|
|
425
|
+
const { password } = formValues;
|
|
426
|
+
return services.handleSignIn({ username, password });
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
// Passwordless flow using USER_AUTH
|
|
430
|
+
return services.handleSignIn({
|
|
431
|
+
username,
|
|
432
|
+
options: {
|
|
433
|
+
authFlowType: 'USER_AUTH',
|
|
434
|
+
preferredChallenge: method,
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
}
|
|
288
438
|
},
|
|
289
439
|
confirmSignIn({ formValues, step }) {
|
|
290
440
|
const formValuesKey = getConfirmSignInFormValuesKey(step);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createMachine, sendUpdate } from 'xstate';
|
|
2
|
-
import { signInWithRedirect, fetchUserAttributes, autoSignIn } from 'aws-amplify/auth';
|
|
2
|
+
import { listWebAuthnCredentials, signInWithRedirect, fetchUserAttributes, autoSignIn } from 'aws-amplify/auth';
|
|
3
3
|
import { getSignUpInput } from '../utils.mjs';
|
|
4
4
|
import { runValidators } from '../../../validators/index.mjs';
|
|
5
5
|
import ACTIONS from '../actions.mjs';
|
|
@@ -50,26 +50,6 @@ const handleAutoSignInResponse = {
|
|
|
50
50
|
target: '#signUpActor.resolved',
|
|
51
51
|
},
|
|
52
52
|
};
|
|
53
|
-
const handleFetchUserAttributesResponse = {
|
|
54
|
-
onDone: [
|
|
55
|
-
{
|
|
56
|
-
cond: 'shouldVerifyAttribute',
|
|
57
|
-
actions: [
|
|
58
|
-
'setShouldVerifyUserAttributeStep',
|
|
59
|
-
'setUnverifiedUserAttributes',
|
|
60
|
-
],
|
|
61
|
-
target: '#signUpActor.resolved',
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
actions: 'setConfirmAttributeCompleteStep',
|
|
65
|
-
target: '#signUpActor.resolved',
|
|
66
|
-
},
|
|
67
|
-
],
|
|
68
|
-
onError: {
|
|
69
|
-
actions: 'setConfirmAttributeCompleteStep',
|
|
70
|
-
target: '#signUpActor.resolved',
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
53
|
function signUpActor({ services }) {
|
|
74
54
|
return createMachine({
|
|
75
55
|
id: 'signUpActor',
|
|
@@ -89,10 +69,65 @@ function signUpActor({ services }) {
|
|
|
89
69
|
fetchUserAttributes: {
|
|
90
70
|
invoke: {
|
|
91
71
|
src: 'fetchUserAttributes',
|
|
92
|
-
|
|
72
|
+
onDone: [
|
|
73
|
+
{
|
|
74
|
+
cond: 'hasPasskeyRegistrationPrompts',
|
|
75
|
+
actions: 'setFetchedUserAttributes',
|
|
76
|
+
target: 'checkPasskeys',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
actions: 'setFetchedUserAttributes',
|
|
80
|
+
target: 'evaluatePasskeyPrompt',
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
onError: {
|
|
84
|
+
actions: 'setConfirmAttributeCompleteStep',
|
|
85
|
+
target: '#signUpActor.resolved',
|
|
86
|
+
},
|
|
93
87
|
},
|
|
94
88
|
},
|
|
95
|
-
|
|
89
|
+
checkPasskeys: {
|
|
90
|
+
invoke: {
|
|
91
|
+
src: async () => {
|
|
92
|
+
try {
|
|
93
|
+
const result = await listWebAuthnCredentials();
|
|
94
|
+
return result.credentials && result.credentials.length > 0;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
onDone: {
|
|
101
|
+
actions: 'setHasExistingPasskeys',
|
|
102
|
+
target: 'evaluatePasskeyPrompt',
|
|
103
|
+
},
|
|
104
|
+
onError: {
|
|
105
|
+
actions: 'clearHasExistingPasskeys',
|
|
106
|
+
target: 'evaluatePasskeyPrompt',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
evaluatePasskeyPrompt: {
|
|
111
|
+
always: [
|
|
112
|
+
{
|
|
113
|
+
cond: 'shouldPromptPasskeyRegistrationAfterSignup',
|
|
114
|
+
target: '#signUpActor.passkeyPrompt',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
cond: 'shouldVerifyAttribute',
|
|
118
|
+
actions: [
|
|
119
|
+
'setShouldVerifyUserAttributeStep',
|
|
120
|
+
'setUnverifiedUserAttributes',
|
|
121
|
+
],
|
|
122
|
+
target: '#signUpActor.resolved',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
actions: 'setConfirmAttributeCompleteStep',
|
|
126
|
+
target: '#signUpActor.resolved',
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
federatedSignIn: { ...getFederatedSignInState('signUp') },
|
|
96
131
|
resetPassword: {
|
|
97
132
|
invoke: { src: 'resetPassword', ...handleResetPasswordResponse },
|
|
98
133
|
},
|
|
@@ -111,7 +146,10 @@ function signUpActor({ services }) {
|
|
|
111
146
|
cond: 'isUserAlreadyConfirmed',
|
|
112
147
|
target: '#signUpActor.resolved',
|
|
113
148
|
},
|
|
114
|
-
{
|
|
149
|
+
{
|
|
150
|
+
actions: ['setRemoteError', 'sendUpdate'],
|
|
151
|
+
target: '#signUpActor.confirmSignUp',
|
|
152
|
+
},
|
|
115
153
|
],
|
|
116
154
|
},
|
|
117
155
|
},
|
|
@@ -149,7 +187,13 @@ function signUpActor({ services }) {
|
|
|
149
187
|
idle: {
|
|
150
188
|
entry: ['sendUpdate'],
|
|
151
189
|
on: {
|
|
152
|
-
SUBMIT: {
|
|
190
|
+
SUBMIT: {
|
|
191
|
+
actions: [
|
|
192
|
+
'setSelectedAuthMethodFromForm',
|
|
193
|
+
'handleSubmit',
|
|
194
|
+
],
|
|
195
|
+
target: 'validate',
|
|
196
|
+
},
|
|
153
197
|
},
|
|
154
198
|
},
|
|
155
199
|
validate: {
|
|
@@ -234,6 +278,13 @@ function signUpActor({ services }) {
|
|
|
234
278
|
},
|
|
235
279
|
},
|
|
236
280
|
},
|
|
281
|
+
passkeyPrompt: {
|
|
282
|
+
entry: 'sendUpdate',
|
|
283
|
+
on: {
|
|
284
|
+
SKIP: { target: 'resolved' },
|
|
285
|
+
SUBMIT: { target: 'resolved' },
|
|
286
|
+
},
|
|
287
|
+
},
|
|
237
288
|
resolved: {
|
|
238
289
|
type: 'final',
|
|
239
290
|
data: (context) => ({
|
|
@@ -271,9 +322,10 @@ function signUpActor({ services }) {
|
|
|
271
322
|
return signInWithRedirect(data);
|
|
272
323
|
},
|
|
273
324
|
handleSignUp(context) {
|
|
274
|
-
const { formValues, loginMechanisms, username } = context;
|
|
325
|
+
const { formValues, loginMechanisms, username, selectedAuthMethod, preferredChallenge, } = context;
|
|
275
326
|
const loginMechanism = loginMechanisms[0];
|
|
276
|
-
const
|
|
327
|
+
const authMethod = selectedAuthMethod ?? preferredChallenge;
|
|
328
|
+
const input = getSignUpInput(username, formValues, loginMechanism, authMethod);
|
|
277
329
|
return services.handleSignUp(input);
|
|
278
330
|
},
|
|
279
331
|
async validateSignUp(context) {
|
|
@@ -284,6 +336,8 @@ function signUpActor({ services }) {
|
|
|
284
336
|
// Validation for default form fields
|
|
285
337
|
services.validateConfirmPassword,
|
|
286
338
|
services.validatePreferredUsername,
|
|
339
|
+
// Validation for required fields based on auth method
|
|
340
|
+
services.validateRequiredFieldsForAuthMethod,
|
|
287
341
|
// Validation for any custom Sign Up fields
|
|
288
342
|
services.validateCustomSignUp,
|
|
289
343
|
]);
|
|
@@ -40,11 +40,24 @@ const defaultServices = {
|
|
|
40
40
|
const parsedSocialProviders = loginWith?.oauth?.providers
|
|
41
41
|
? loginWith.oauth.providers?.map((provider) => provider.toString().toLowerCase())
|
|
42
42
|
: undefined;
|
|
43
|
+
// Detect passwordless capabilities from amplify_outputs.json
|
|
44
|
+
// Support both snake_case (legacy) and camelCase (current) formats
|
|
45
|
+
const passwordlessConfig = result.Auth?.Cognito?.passwordless;
|
|
46
|
+
const passwordlessCapabilities = {
|
|
47
|
+
emailOtpEnabled: passwordlessConfig?.emailOtpEnabled ??
|
|
48
|
+
passwordlessConfig?.email_otp_enabled === true,
|
|
49
|
+
smsOtpEnabled: passwordlessConfig?.smsOtpEnabled ??
|
|
50
|
+
passwordlessConfig?.sms_otp_enabled === true,
|
|
51
|
+
webAuthnEnabled: !!(passwordlessConfig?.webAuthn ?? passwordlessConfig?.web_authn),
|
|
52
|
+
preferredChallenge: passwordlessConfig?.preferredChallenge ??
|
|
53
|
+
passwordlessConfig?.preferred_challenge,
|
|
54
|
+
};
|
|
43
55
|
return {
|
|
44
56
|
...cliConfig,
|
|
45
57
|
loginMechanisms: parsedLoginMechanisms,
|
|
46
58
|
signUpAttributes: parsedSignupAttributes,
|
|
47
59
|
socialProviders: parsedSocialProviders,
|
|
60
|
+
passwordlessCapabilities,
|
|
48
61
|
};
|
|
49
62
|
},
|
|
50
63
|
getCurrentUser,
|
|
@@ -107,6 +120,30 @@ const defaultServices = {
|
|
|
107
120
|
}
|
|
108
121
|
},
|
|
109
122
|
async validatePreferredUsername(_, __) { },
|
|
123
|
+
async validateRequiredFieldsForAuthMethod(formData) {
|
|
124
|
+
const authMethod = formData.__authMethod;
|
|
125
|
+
// If no auth method specified, skip validation (will use default required fields)
|
|
126
|
+
if (!authMethod)
|
|
127
|
+
return null;
|
|
128
|
+
// Check required fields based on auth method
|
|
129
|
+
if (authMethod === 'EMAIL_OTP' && !formData.email) {
|
|
130
|
+
return { email: 'Email is required for Email OTP sign up' };
|
|
131
|
+
}
|
|
132
|
+
if (authMethod === 'SMS_OTP' && !formData.phone_number) {
|
|
133
|
+
return { phone_number: 'Phone number is required for SMS OTP sign up' };
|
|
134
|
+
}
|
|
135
|
+
if (authMethod === 'PASSWORD') {
|
|
136
|
+
const errors = {};
|
|
137
|
+
if (!formData.password) {
|
|
138
|
+
errors.password = 'Password is required';
|
|
139
|
+
}
|
|
140
|
+
if (!formData.confirm_password) {
|
|
141
|
+
errors.confirm_password = 'Confirm Password is required';
|
|
142
|
+
}
|
|
143
|
+
return Object.keys(errors).length > 0 ? errors : null;
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
},
|
|
110
147
|
};
|
|
111
148
|
|
|
112
149
|
export { defaultServices };
|
|
@@ -30,7 +30,11 @@ const shouldResetPassword = ({ step }) => step === 'RESET_PASSWORD';
|
|
|
30
30
|
const shouldConfirmResetPassword = ({ step }) => step === 'CONFIRM_RESET_PASSWORD_WITH_CODE';
|
|
31
31
|
const shouldConfirmSignUp = ({ step }) => step === 'CONFIRM_SIGN_UP';
|
|
32
32
|
// miscellaneous guards
|
|
33
|
-
const shouldVerifyAttribute = (
|
|
33
|
+
const shouldVerifyAttribute = (context, event) => {
|
|
34
|
+
// Try to get data from event first (for backward compatibility), then from context
|
|
35
|
+
const data = (event.data || context.fetchedUserAttributes);
|
|
36
|
+
if (!data)
|
|
37
|
+
return false;
|
|
34
38
|
const { email, phone_number, phone_number_verified, email_verified } = data;
|
|
35
39
|
// if neither email nor phone_number exist
|
|
36
40
|
// there is nothing to verify
|
|
@@ -55,11 +59,51 @@ const shouldVerifyAttribute = (_, { data }) => {
|
|
|
55
59
|
* https://github.com/aws-amplify/amplify-ui/issues/219
|
|
56
60
|
*/
|
|
57
61
|
const isUserAlreadyConfirmed = (_, { data }) => data.message === 'User is already confirmed.';
|
|
62
|
+
// passwordless guards
|
|
63
|
+
const shouldSelectAuthMethod = ({ availableAuthMethods, preferredChallenge, selectedAuthMethod, }) => {
|
|
64
|
+
// Show selection if:
|
|
65
|
+
// 1. Multiple methods available
|
|
66
|
+
// 2. AND either no preferredChallenge OR selectedAuthMethod is explicitly cleared (null)
|
|
67
|
+
const hasMultipleMethods = availableAuthMethods && availableAuthMethods.length > 1;
|
|
68
|
+
const shouldShowSelection = !preferredChallenge || selectedAuthMethod === null;
|
|
69
|
+
return hasMultipleMethods && shouldShowSelection;
|
|
70
|
+
};
|
|
71
|
+
const shouldPromptPasskeyRegistration = ({ passwordless, hasExistingPasskeys, }) => {
|
|
72
|
+
const { passkeyRegistrationPrompts } = passwordless || {};
|
|
73
|
+
if (!passkeyRegistrationPrompts) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
// Don't prompt if user already has passkeys
|
|
77
|
+
if (hasExistingPasskeys) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
if (typeof passkeyRegistrationPrompts === 'boolean') {
|
|
81
|
+
return passkeyRegistrationPrompts;
|
|
82
|
+
}
|
|
83
|
+
return passkeyRegistrationPrompts.afterSignin === 'ALWAYS';
|
|
84
|
+
};
|
|
85
|
+
const shouldPromptPasskeyRegistrationAfterSignup = ({ passwordless, hasExistingPasskeys, }) => {
|
|
86
|
+
const { passkeyRegistrationPrompts } = passwordless || {};
|
|
87
|
+
if (!passkeyRegistrationPrompts) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
// Don't prompt if user already has passkeys
|
|
91
|
+
if (hasExistingPasskeys) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (typeof passkeyRegistrationPrompts === 'boolean') {
|
|
95
|
+
return passkeyRegistrationPrompts;
|
|
96
|
+
}
|
|
97
|
+
return passkeyRegistrationPrompts.afterSignup === 'ALWAYS';
|
|
98
|
+
};
|
|
99
|
+
const hasPasskeyRegistrationPrompts = ({ passwordless }) => passwordless?.passkeyRegistrationPrompts != null;
|
|
100
|
+
const shouldReturnToSelectMethod = ({ selectedAuthMethod, step, }) => selectedAuthMethod != null && step === 'SELECT_AUTH_METHOD';
|
|
58
101
|
const GUARDS = {
|
|
59
102
|
hasCompletedAttributeConfirmation,
|
|
60
103
|
hasCompletedResetPassword,
|
|
61
104
|
hasCompletedSignIn,
|
|
62
105
|
hasCompletedSignUp,
|
|
106
|
+
hasPasskeyRegistrationPrompts,
|
|
63
107
|
isConfirmSignUpStep,
|
|
64
108
|
isConfirmUserAttributeStep,
|
|
65
109
|
isResetPasswordStep,
|
|
@@ -73,10 +117,14 @@ const GUARDS = {
|
|
|
73
117
|
shouldConfirmSignUpFromSignIn,
|
|
74
118
|
shouldResetPassword,
|
|
75
119
|
shouldResetPasswordFromSignIn,
|
|
120
|
+
shouldReturnToSelectMethod,
|
|
121
|
+
shouldSelectAuthMethod,
|
|
76
122
|
shouldSetupTotp,
|
|
77
123
|
shouldSetupEmail,
|
|
78
124
|
shouldSelectMfaType,
|
|
79
125
|
shouldVerifyAttribute,
|
|
126
|
+
shouldPromptPasskeyRegistration,
|
|
127
|
+
shouldPromptPasskeyRegistrationAfterSignup,
|
|
80
128
|
};
|
|
81
129
|
|
|
82
130
|
export { GUARDS as default };
|