@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.
Files changed (28) hide show
  1. package/dist/esm/helpers/authenticator/facade.mjs +9 -2
  2. package/dist/esm/helpers/authenticator/formFields/defaults.mjs +34 -8
  3. package/dist/esm/helpers/authenticator/getRoute.mjs +8 -0
  4. package/dist/esm/helpers/authenticator/textUtil.mjs +34 -0
  5. package/dist/esm/i18n/dictionaries/authenticator/defaultTexts.mjs +27 -0
  6. package/dist/esm/i18n/dictionaries/authenticator/es.mjs +1 -1
  7. package/dist/esm/i18n/dictionaries/authenticator/fr.mjs +1 -1
  8. package/dist/esm/machines/authenticator/actions.mjs +63 -4
  9. package/dist/esm/machines/authenticator/actors/signIn.mjs +199 -49
  10. package/dist/esm/machines/authenticator/actors/signUp.mjs +81 -27
  11. package/dist/esm/machines/authenticator/defaultServices.mjs +37 -0
  12. package/dist/esm/machines/authenticator/guards.mjs +49 -1
  13. package/dist/esm/machines/authenticator/index.mjs +29 -15
  14. package/dist/esm/machines/authenticator/utils.mjs +58 -6
  15. package/dist/index.js +625 -111
  16. package/dist/styles/authenticator.css +17 -0
  17. package/dist/styles/authenticator.layer.css +17 -0
  18. package/dist/styles.css +17 -0
  19. package/dist/styles.layer.css +17 -0
  20. package/dist/types/helpers/authenticator/facade.d.ts +6 -2
  21. package/dist/types/helpers/authenticator/textUtil.d.ts +24 -1
  22. package/dist/types/i18n/dictionaries/authenticator/defaultTexts.d.ts +27 -0
  23. package/dist/types/i18n/dictionaries/index.d.ts +27 -0
  24. package/dist/types/i18n/translations.d.ts +27 -0
  25. package/dist/types/machines/authenticator/defaultServices.d.ts +105 -0
  26. package/dist/types/machines/authenticator/types.d.ts +26 -2
  27. package/dist/types/machines/authenticator/utils.d.ts +14 -2
  28. package/package.json +5 -4
@@ -10,21 +10,31 @@ import { signUpActor } from './actors/signUp.mjs';
10
10
  import { signOutActor } from './actors/signOut.mjs';
11
11
  import { verifyUserAttributesActor } from './actors/verifyUserAttributes.mjs';
12
12
  import { defaultServices } from './defaultServices.mjs';
13
+ import { getAvailableAuthMethods } from './utils.mjs';
13
14
 
14
- const getActorContext = (context, defaultStep) => ({
15
- ...context.actorDoneData,
16
- step: context?.actorDoneData?.step ?? defaultStep,
17
- // initialize empty objects on actor start
18
- formValues: {},
19
- touched: {},
20
- validationError: {},
21
- // values included on `context.config` that should be available in actors
22
- formFields: context.config?.formFields,
23
- loginMechanisms: context.config?.loginMechanisms,
24
- passwordSettings: context.config?.passwordSettings,
25
- signUpAttributes: context.config?.signUpAttributes,
26
- socialProviders: context.config?.socialProviders,
27
- });
15
+ const getActorContext = (context, defaultStep) => {
16
+ const availableAuthMethods = getAvailableAuthMethods(context.passwordlessCapabilities, context.config?.passwordless?.hiddenAuthMethods);
17
+ // Determine effective preferred challenge: component prop takes precedence over backend config
18
+ const preferredChallenge = context.config?.passwordless?.preferredAuthMethod ??
19
+ context.passwordlessCapabilities?.preferredChallenge;
20
+ return {
21
+ ...context.actorDoneData,
22
+ step: context?.actorDoneData?.step ?? defaultStep,
23
+ // initialize empty objects on actor start
24
+ formValues: {},
25
+ touched: {},
26
+ validationError: {},
27
+ // values included on `context.config` that should be available in actors
28
+ formFields: context.config?.formFields,
29
+ loginMechanisms: context.config?.loginMechanisms,
30
+ passwordSettings: context.config?.passwordSettings,
31
+ signUpAttributes: context.config?.signUpAttributes,
32
+ socialProviders: context.config?.socialProviders,
33
+ availableAuthMethods,
34
+ preferredChallenge,
35
+ passwordless: context.config?.passwordless,
36
+ };
37
+ };
28
38
  const { choose, stop } = actions;
29
39
  const stopActor = (machineId) => stop(machineId);
30
40
  // setup step waits for ui to emit INIT action to proceed to configure
@@ -122,6 +132,8 @@ function createAuthenticatorMachine(options) {
122
132
  },
123
133
  on: {
124
134
  FORGOT_PASSWORD: 'forgotPasswordActor',
135
+ SELECT_METHOD: { actions: 'forwardToActor' },
136
+ SHOW_AUTH_METHODS: { actions: 'forwardToActor' },
125
137
  SIGN_IN: 'signInActor',
126
138
  SIGN_UP: 'signUpActor',
127
139
  'done.invoke.signInActor': [
@@ -288,10 +300,11 @@ function createAuthenticatorMachine(options) {
288
300
  }),
289
301
  }),
290
302
  applyAmplifyConfig: assign({
303
+ passwordlessCapabilities: (_, { data: cliConfig }) => cliConfig.passwordlessCapabilities,
291
304
  config(context, { data: cliConfig }) {
292
305
  // Prefer explicitly configured settings over default CLI values\
293
306
  const { loginMechanisms = cliConfig.loginMechanisms ?? [], signUpAttributes = cliConfig.signUpAttributes ?? [], socialProviders = cliConfig.socialProviders ?? [], initialState, formFields: _formFields, passwordSettings = cliConfig.passwordFormat ??
294
- {}, } = context.config;
307
+ {}, passwordless, } = context.config;
295
308
  // By default, Cognito assumes `username`, so there isn't a different username attribute like `email`.
296
309
  // We explicitly add it as a login mechanism to be consistent with Admin UI's language.
297
310
  if (loginMechanisms.length === 0) {
@@ -303,6 +316,7 @@ function createAuthenticatorMachine(options) {
303
316
  initialState,
304
317
  loginMechanisms,
305
318
  passwordSettings,
319
+ passwordless,
306
320
  signUpAttributes,
307
321
  socialProviders,
308
322
  };
@@ -46,21 +46,44 @@ const getUserAttributes = (formValues) => {
46
46
  }
47
47
  return userAttributes;
48
48
  };
49
- const getSignUpInput = (username, formValues, loginMechanism) => {
49
+ const getSignUpInput = (username, formValues, loginMechanism, authMethod) => {
50
50
  const { password, ...values } = formValues;
51
51
  const attributes = getUserAttributes(values);
52
+ const isPasswordless = authMethod && authMethod !== 'PASSWORD';
53
+ // For SMS OTP, set the email channel to empty string if present to force account creation with phone number
54
+ let userAttributes = attributes;
55
+ if (authMethod === 'SMS_OTP' && attributes.email) {
56
+ userAttributes = { ...attributes, email: '' };
57
+ }
52
58
  const options = {
53
- autoSignIn: DEFAULT_AUTO_SIGN_IN,
59
+ autoSignIn: isPasswordless
60
+ ? {
61
+ enabled: true,
62
+ authFlowType: 'USER_AUTH',
63
+ preferredChallenge: authMethod,
64
+ }
65
+ : DEFAULT_AUTO_SIGN_IN,
54
66
  userAttributes: {
55
67
  // use `username` value for `phone_number`
56
68
  ...(loginMechanism === 'phone_number'
57
- ? { ...attributes, phone_number: username }
58
- : attributes),
69
+ ? { ...userAttributes, phone_number: username }
70
+ : userAttributes),
59
71
  },
60
72
  };
61
- return { username, password, options };
73
+ return { username, password: isPasswordless ? undefined : password, options };
62
74
  };
63
75
  const getUsernameSignUp = ({ formValues, loginMechanisms, }) => {
76
+ // Check if a specific auth method was selected via form data
77
+ const authMethod = formValues.__authMethod;
78
+ // For SMS_OTP, always use phone_number as username
79
+ if (authMethod === 'SMS_OTP') {
80
+ const { country_code, phone_number } = formValues;
81
+ return sanitizePhoneNumber(country_code, phone_number);
82
+ }
83
+ // For EMAIL_OTP, always use email as username
84
+ if (authMethod === 'EMAIL_OTP') {
85
+ return formValues.email;
86
+ }
64
87
  // When 'username' is in loginMechanisms, always use the username field for the Username parameter.
65
88
  // This handles both username-only mode and alias mode (username + email/phone as sign-in options).
66
89
  // See: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html#user-pool-settings-aliases
@@ -75,5 +98,34 @@ const getUsernameSignUp = ({ formValues, loginMechanisms, }) => {
75
98
  // Otherwise, use the primary login mechanism (email as username)
76
99
  return formValues[loginMechanism];
77
100
  };
101
+ /**
102
+ * Get available authentication methods based on backend capabilities and hidden methods
103
+ */
104
+ const getAvailableAuthMethods = (passwordlessCapabilities, hiddenAuthMethods) => {
105
+ const allMethods = [];
106
+ // If hiddenAuthMethods is explicitly provided (even as empty array),
107
+ // assume all methods are available and let hiddenAuthMethods filter them
108
+ const assumeAllAvailable = hiddenAuthMethods !== undefined;
109
+ // PASSWORD is always available by default
110
+ allMethods.push('PASSWORD');
111
+ // Add passwordless methods if backend supports them OR if hiddenAuthMethods is explicitly set
112
+ if (assumeAllAvailable || passwordlessCapabilities?.emailOtpEnabled) {
113
+ allMethods.push('EMAIL_OTP');
114
+ }
115
+ if (assumeAllAvailable || passwordlessCapabilities?.smsOtpEnabled) {
116
+ allMethods.push('SMS_OTP');
117
+ }
118
+ if (assumeAllAvailable || passwordlessCapabilities?.webAuthnEnabled) {
119
+ allMethods.push('WEB_AUTHN');
120
+ }
121
+ // Filter out hidden methods
122
+ const hidden = hiddenAuthMethods ?? [];
123
+ const availableMethods = allMethods.filter((method) => !hidden.includes(method));
124
+ // Validate that at least one method remains
125
+ if (availableMethods.length === 0) {
126
+ throw new Error('InvalidPasswordlessSettings: All authentication methods are hidden');
127
+ }
128
+ return availableMethods;
129
+ };
78
130
 
79
- export { getSignUpInput, getUserAttributes, getUsernameSignUp, sanitizePhoneNumber };
131
+ export { getAvailableAuthMethods, getSignUpInput, getUserAttributes, getUsernameSignUp, sanitizePhoneNumber };