@aws-amplify/ui-react 6.13.2 → 6.15.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 (37) hide show
  1. package/dist/{Field-BjInyX0G.js → Field-Cq088Vbv.js} +38 -0
  2. package/dist/esm/components/Authenticator/Authenticator.mjs +2 -1
  3. package/dist/esm/components/Authenticator/ConfirmSignIn/ConfirmSignIn.mjs +16 -4
  4. package/dist/esm/components/Authenticator/PasskeyPrompt/PasskeyPrompt.mjs +109 -0
  5. package/dist/esm/components/Authenticator/Router/Router.mjs +6 -0
  6. package/dist/esm/components/Authenticator/SignIn/SignIn.mjs +46 -4
  7. package/dist/esm/components/Authenticator/SignInSelectAuthFactor/SignInSelectAuthFactor.mjs +116 -0
  8. package/dist/esm/components/Authenticator/SignUp/SignUp.mjs +55 -4
  9. package/dist/esm/components/Authenticator/shared/ConfirmSignInFooter.mjs +7 -2
  10. package/dist/esm/components/Authenticator/shared/FormFields.mjs +18 -5
  11. package/dist/esm/components/Authenticator/utils.mjs +8 -1
  12. package/dist/esm/internal.mjs +2 -0
  13. package/dist/esm/primitives/Icon/icons/IconCheckCircleFill.mjs +15 -0
  14. package/dist/esm/primitives/Icon/icons/IconPasskey.mjs +31 -0
  15. package/dist/esm/version.mjs +1 -1
  16. package/dist/index.js +305 -36
  17. package/dist/internal.js +3 -1
  18. package/dist/styles/StorageBrowser.css +0 -1
  19. package/dist/styles/StorageBrowser.layer.css +0 -1
  20. package/dist/styles/authenticator.css +17 -0
  21. package/dist/styles/authenticator.layer.css +17 -0
  22. package/dist/styles/modal.css +81 -0
  23. package/dist/styles/modal.layer.css +83 -0
  24. package/dist/styles.css +100 -1
  25. package/dist/styles.layer.css +100 -1
  26. package/dist/types/components/Authenticator/Authenticator.d.ts +1 -1
  27. package/dist/types/components/Authenticator/PasskeyPrompt/PasskeyPrompt.d.ts +3 -0
  28. package/dist/types/components/Authenticator/PasskeyPrompt/index.d.ts +1 -0
  29. package/dist/types/components/Authenticator/SignInSelectAuthFactor/SignInSelectAuthFactor.d.ts +2 -0
  30. package/dist/types/components/Authenticator/SignInSelectAuthFactor/index.d.ts +1 -0
  31. package/dist/types/components/Authenticator/shared/FormFields.d.ts +3 -1
  32. package/dist/types/components/Authenticator/utils.d.ts +6 -0
  33. package/dist/types/primitives/Icon/icons/IconCheckCircleFill.d.ts +5 -0
  34. package/dist/types/primitives/Icon/icons/IconPasskey.d.ts +5 -0
  35. package/dist/types/primitives/Icon/icons/index.d.ts +2 -0
  36. package/dist/types/version.d.ts +1 -1
  37. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -6,17 +6,17 @@ var React = require('react');
6
6
  var isEqual = require('lodash/isEqual.js');
7
7
  var uiReactCore = require('@aws-amplify/ui-react-core');
8
8
  var ui = require('@aws-amplify/ui');
9
- var Field = require('./Field-BjInyX0G.js');
9
+ var Field = require('./Field-Cq088Vbv.js');
10
10
  require('aws-amplify/storage');
11
11
  var debounce = require('lodash/debounce.js');
12
12
  var reactDropdownMenu = require('@radix-ui/react-dropdown-menu');
13
13
  var reactSlider = require('@radix-ui/react-slider');
14
14
  var QRCode = require('qrcode');
15
15
  var utils = require('aws-amplify/utils');
16
+ var auth = require('aws-amplify/auth');
16
17
  var reactDirection = require('@radix-ui/react-direction');
17
18
  var ThemeStyle = require('./ThemeStyle-DA2-Clfz.js');
18
19
  require('@aws-amplify/core');
19
- require('aws-amplify/auth');
20
20
 
21
21
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
22
22
 
@@ -2472,7 +2472,7 @@ const defaultDeleteUserDisplayText = {
2472
2472
  warningText: 'Deleting your account is not reversible. You will lose access to your account and all data associated with it.',
2473
2473
  };
2474
2474
 
2475
- const VERSION = '6.13.2';
2475
+ const VERSION = '6.15.0';
2476
2476
 
2477
2477
  const logger$2 = ui.getLogger('AccountSettings');
2478
2478
  const getIsDisabled = (formValues, validationError) => {
@@ -2794,17 +2794,30 @@ function FormField({ autocomplete: autoComplete, dialCode, name, type, ...props
2794
2794
  }
2795
2795
  }
2796
2796
 
2797
- function FormFields() {
2797
+ function FormFields({ includePassword, } = {}) {
2798
2798
  const { fields } = uiReactCore.useAuthenticator(({ route }) => [route]);
2799
- const formFields = React__namespace.useRef(fields.map((field, index) => (React__namespace.createElement(FormField
2800
- // use index for key, field order is static
2801
- , {
2799
+ const { selectedAuthMethod, preferredChallenge } = uiReactCore.useAuthenticator((context) => [context.selectedAuthMethod, context.preferredChallenge]);
2800
+ // Determine if password should be shown
2801
+ const effectiveMethod = selectedAuthMethod ?? preferredChallenge;
2802
+ const shouldRequirePassword = includePassword ?? (!effectiveMethod || effectiveMethod === 'PASSWORD');
2803
+ const formFields = React__namespace.useRef(fields
2804
+ .map((field, index) => {
2805
+ // Make password and confirm_password optional for passwordless methods
2806
+ if ((field.name === 'password' || field.name === 'confirm_password') &&
2807
+ !shouldRequirePassword) {
2808
+ return (React__namespace.createElement(FormField, { key: index, ...field, isRequired: false }));
2809
+ }
2810
+ return (React__namespace.createElement(FormField
2802
2811
  // use index for key, field order is static
2803
- key: index, ...field })))).current;
2812
+ , {
2813
+ // use index for key, field order is static
2814
+ key: index, ...field }));
2815
+ })
2816
+ .filter(Boolean)).current;
2804
2817
  return React__namespace.createElement(React__namespace.Fragment, null, formFields);
2805
2818
  }
2806
2819
 
2807
- const { getDeliveryMessageText, getDeliveryMethodText, getConfirmingText: getConfirmingText$1, getConfirmText: getConfirmText$1, getResendCodeText: getResendCodeText$1, } = ui.authenticatorTextUtil;
2820
+ const { getDeliveryMessageText: getDeliveryMessageText$1, getDeliveryMethodText: getDeliveryMethodText$1, getConfirmingText: getConfirmingText$1, getConfirmText: getConfirmText$1, getResendCodeText: getResendCodeText$2, } = ui.authenticatorTextUtil;
2808
2821
  function ConfirmSignUp({ className, variation, }) {
2809
2822
  const { isPending, resendCode, codeDeliveryDetails } = uiReactCore.useAuthenticator((context) => [
2810
2823
  context.isPending,
@@ -2822,18 +2835,18 @@ function ConfirmSignUp({ className, variation, }) {
2822
2835
  React__namespace["default"].createElement(Field.Flex, { as: "fieldset", direction: "column", isDisabled: isPending },
2823
2836
  React__namespace["default"].createElement(Header, null),
2824
2837
  React__namespace["default"].createElement(Field.Flex, { direction: "column" },
2825
- React__namespace["default"].createElement(Field.Text, { className: "amplify-authenticator__subtitle" }, getDeliveryMessageText(codeDeliveryDetails)),
2838
+ React__namespace["default"].createElement(Field.Text, { className: "amplify-authenticator__subtitle" }, getDeliveryMessageText$1(codeDeliveryDetails)),
2826
2839
  React__namespace["default"].createElement(FormFields, null),
2827
2840
  React__namespace["default"].createElement(RemoteErrorMessage, null),
2828
2841
  React__namespace["default"].createElement(Field.Button, { variation: "primary", isDisabled: isPending, type: "submit", loadingText: getConfirmingText$1(), isLoading: isPending }, getConfirmText$1()),
2829
- React__namespace["default"].createElement(Field.Button, { onClick: resendCode, type: "button" }, getResendCodeText$1())),
2842
+ React__namespace["default"].createElement(Field.Button, { onClick: resendCode, type: "button" }, getResendCodeText$2())),
2830
2843
  React__namespace["default"].createElement(Footer, null)))));
2831
2844
  }
2832
2845
  const DefaultHeader = () => {
2833
2846
  const { codeDeliveryDetails } = uiReactCore.useAuthenticator((context) => [
2834
2847
  context.codeDeliveryDetails,
2835
2848
  ]);
2836
- return (React__namespace["default"].createElement(Heading, { level: 4 }, getDeliveryMethodText(codeDeliveryDetails)));
2849
+ return (React__namespace["default"].createElement(Heading, { level: 4 }, getDeliveryMethodText$1(codeDeliveryDetails)));
2837
2850
  };
2838
2851
  ConfirmSignUp.Header = DefaultHeader;
2839
2852
  ConfirmSignUp.Footer = function Footer() {
@@ -2841,7 +2854,7 @@ ConfirmSignUp.Footer = function Footer() {
2841
2854
  return null;
2842
2855
  };
2843
2856
 
2844
- const { getChangePasswordText, getChangingText, getBackToSignInText: getBackToSignInText$2 } = ui.authenticatorTextUtil;
2857
+ const { getChangePasswordText, getChangingText, getBackToSignInText: getBackToSignInText$3 } = ui.authenticatorTextUtil;
2845
2858
  const ForceNewPassword = ({ className, variation, }) => {
2846
2859
  const { isPending, toSignIn } = uiReactCore.useAuthenticator((context) => [
2847
2860
  context.isPending,
@@ -2858,7 +2871,7 @@ const ForceNewPassword = ({ className, variation, }) => {
2858
2871
  React__namespace["default"].createElement(FormFields, null),
2859
2872
  React__namespace["default"].createElement(RemoteErrorMessage, null),
2860
2873
  React__namespace["default"].createElement(Field.Button, { isDisabled: isPending, type: "submit", variation: "primary", isLoading: isPending, loadingText: getChangingText() }, getChangePasswordText()),
2861
- React__namespace["default"].createElement(Field.Button, { onClick: toSignIn, type: "button", variation: "link", size: "small" }, getBackToSignInText$2()),
2874
+ React__namespace["default"].createElement(Field.Button, { onClick: toSignIn, type: "button", variation: "link", size: "small" }, getBackToSignInText$3()),
2862
2875
  React__namespace["default"].createElement(Footer, null)))));
2863
2876
  };
2864
2877
  ForceNewPassword.FormFields = function FormFields$1() {
@@ -2871,15 +2884,28 @@ ForceNewPassword.Footer = function Footer() {
2871
2884
  return null;
2872
2885
  };
2873
2886
 
2874
- const { getConfirmText, getConfirmingText, getBackToSignInText: getBackToSignInText$1 } = ui.authenticatorTextUtil;
2887
+ const isSignInOrSignUpRoute = (route) => route === 'signIn' || route === 'signUp';
2888
+ /**
2889
+ * Checks if challenge is OTP
2890
+ * @param challengeName - The challenge name (e.g., 'EMAIL_OTP', 'SMS_MFA')
2891
+ * @returns true if challenge is OTP
2892
+ */
2893
+ const isOtpChallenge = (challengeName) => !!challengeName &&
2894
+ (challengeName === 'EMAIL_OTP' || challengeName === 'SMS_MFA');
2895
+
2896
+ const { getConfirmText, getConfirmingText, getBackToSignInText: getBackToSignInText$2, getResendCodeText: getResendCodeText$1, } = ui.authenticatorTextUtil;
2875
2897
  const ConfirmSignInFooter = () => {
2876
- const { isPending, toSignIn } = uiReactCore.useAuthenticator((context) => [
2898
+ const { isPending, toSignIn, challengeName, resendCode } = uiReactCore.useAuthenticator((context) => [
2877
2899
  context.isPending,
2878
2900
  context.toSignIn,
2901
+ context.challengeName,
2902
+ context.resendCode,
2879
2903
  ]);
2904
+ const showResendCode = isOtpChallenge(challengeName) && resendCode;
2880
2905
  return (React__namespace["default"].createElement(Field.Flex, { direction: "column" },
2881
2906
  React__namespace["default"].createElement(Field.Button, { isDisabled: isPending, type: "submit", variation: "primary", isLoading: isPending, loadingText: getConfirmingText() }, getConfirmText()),
2882
- React__namespace["default"].createElement(Field.Button, { onClick: toSignIn, type: "button", variation: "link", size: "small" }, getBackToSignInText$1())));
2907
+ showResendCode && (React__namespace["default"].createElement(Field.Button, { onClick: resendCode, type: "button" }, getResendCodeText$1())),
2908
+ React__namespace["default"].createElement(Field.Button, { onClick: toSignIn, type: "button", variation: "link", size: "small" }, getBackToSignInText$2())));
2883
2909
  };
2884
2910
 
2885
2911
  const logger = new utils.ConsoleLogger('SetupTotp-logger');
@@ -3016,7 +3042,7 @@ const FederatedSignInButton = (props) => {
3016
3042
  React__namespace["default"].createElement(Field.Text, { as: "span" }, text)));
3017
3043
  };
3018
3044
 
3019
- const { getSignInWithFederationText, getOrText } = ui.authenticatorTextUtil;
3045
+ const { getSignInWithFederationText, getOrText: getOrText$2 } = ui.authenticatorTextUtil;
3020
3046
  function FederatedSignIn() {
3021
3047
  const { route, socialProviders } = uiReactCore.useAuthenticator(({ route, socialProviders }) => [route, socialProviders]);
3022
3048
  if (socialProviders.length === 0) {
@@ -3039,19 +3065,60 @@ function FederatedSignIn() {
3039
3065
  console.error(`Authenticator does not support ${provider}. Please open an issue: https://github.com/aws-amplify/amplify-ui/issues/choose`);
3040
3066
  }
3041
3067
  }),
3042
- React__namespace["default"].createElement(Divider, { size: "small", label: getOrText() })));
3068
+ React__namespace["default"].createElement(Divider, { size: "small", label: getOrText$2() })));
3043
3069
  }
3044
3070
 
3045
- const { getSignInText, getSigningInText, getForgotPasswordText } = ui.authenticatorTextUtil;
3071
+ const { getSignInText, getSigningInText: getSigningInText$1, getForgotPasswordText, getSignInWithEmailText: getSignInWithEmailText$1, getSignInWithSmsText: getSignInWithSmsText$1, getSignInWithPasskeyText: getSignInWithPasskeyText$1, getOtherSignInOptionsText, getEnterUsernameFirstText, } = ui.authenticatorTextUtil;
3046
3072
  function SignIn() {
3047
- const { isPending } = uiReactCore.useAuthenticator((context) => [context.isPending]);
3073
+ const { isPending, availableAuthMethods, preferredChallenge, toShowAuthMethods, } = uiReactCore.useAuthenticator((context) => [
3074
+ context.isPending,
3075
+ context.availableAuthMethods,
3076
+ context.preferredChallenge,
3077
+ context.toShowAuthMethods,
3078
+ ]);
3048
3079
  const { handleChange, handleSubmit } = useFormHandlers();
3080
+ const [usernameValue, setUsernameValue] = React__namespace["default"].useState('');
3049
3081
  const { components: {
3050
3082
  // @ts-ignore
3051
3083
  SignIn: { Header = SignIn.Header, Footer = SignIn.Footer }, }, } = useCustomComponents();
3084
+ const hasMultipleMethods = availableAuthMethods && availableAuthMethods.length > 1;
3085
+ const showPreferredButton = hasMultipleMethods && preferredChallenge;
3086
+ const hasUsername = usernameValue.trim().length > 0;
3087
+ const handleFormChange = React__namespace["default"].useCallback((e) => {
3088
+ const { name, value } = e.target;
3089
+ // Track username for "Other sign-in options" button validation
3090
+ if (name === 'username' && typeof value === 'string') {
3091
+ setUsernameValue(value);
3092
+ }
3093
+ handleChange(e);
3094
+ }, [handleChange]);
3095
+ const getPreferredButtonText = () => {
3096
+ if (!preferredChallenge)
3097
+ return getSignInText();
3098
+ switch (preferredChallenge) {
3099
+ case 'EMAIL_OTP':
3100
+ return getSignInWithEmailText$1();
3101
+ case 'SMS_OTP':
3102
+ return getSignInWithSmsText$1();
3103
+ case 'WEB_AUTHN':
3104
+ return getSignInWithPasskeyText$1();
3105
+ case 'PASSWORD':
3106
+ default:
3107
+ return getSignInText();
3108
+ }
3109
+ };
3110
+ const handlePreferredMethodClick = (e) => {
3111
+ e.preventDefault();
3112
+ // For passwordless methods, just submit - the actor already knows the preferredChallenge
3113
+ handleSubmit(e);
3114
+ };
3115
+ const handleOtherOptionsClick = (e) => {
3116
+ e.preventDefault();
3117
+ toShowAuthMethods();
3118
+ };
3052
3119
  return (React__namespace["default"].createElement(Field.View, null,
3053
3120
  React__namespace["default"].createElement(Header, null),
3054
- React__namespace["default"].createElement("form", { "data-amplify-form": "", "data-amplify-authenticator-signin": "", method: "post", onSubmit: handleSubmit, onChange: handleChange },
3121
+ React__namespace["default"].createElement("form", { "data-amplify-form": "", "data-amplify-authenticator-signin": "", method: "post", onSubmit: showPreferredButton ? handlePreferredMethodClick : handleSubmit, onChange: handleFormChange },
3055
3122
  React__namespace["default"].createElement(FederatedSignIn, null),
3056
3123
  React__namespace["default"].createElement(Field.Flex, { direction: "column" },
3057
3124
  React__namespace["default"].createElement(Field.Flex, { as: "fieldset", direction: "column", isDisabled: isPending },
@@ -3059,7 +3126,8 @@ function SignIn() {
3059
3126
  React__namespace["default"].createElement("legend", null, getSignInText())),
3060
3127
  React__namespace["default"].createElement(FormFields, null)),
3061
3128
  React__namespace["default"].createElement(RemoteErrorMessage, null),
3062
- React__namespace["default"].createElement(Field.Button, { isDisabled: isPending, type: "submit", variation: "primary", isLoading: isPending, loadingText: getSigningInText() }, getSignInText()),
3129
+ React__namespace["default"].createElement(Field.Button, { isDisabled: isPending, type: "submit", variation: "primary", isLoading: isPending, loadingText: getSigningInText$1() }, showPreferredButton ? getPreferredButtonText() : getSignInText()),
3130
+ showPreferredButton && (React__namespace["default"].createElement(Field.Button, { onClick: handleOtherOptionsClick, size: "small", variation: "link", isDisabled: isPending || !hasUsername, title: !hasUsername ? getEnterUsernameFirstText() : undefined }, getOtherSignInOptionsText())),
3063
3131
  React__namespace["default"].createElement(Footer, null)))));
3064
3132
  }
3065
3133
  const DefaultFooter = () => {
@@ -3075,25 +3143,76 @@ SignIn.Header = function Header() {
3075
3143
  return null;
3076
3144
  };
3077
3145
 
3078
- const { getCreateAccountText, getCreatingAccountText } = ui.authenticatorTextUtil;
3146
+ const { getCreateAccountText, getCreatingAccountText, getCreateAccountWithEmailText, getCreateAccountWithPasswordText, getCreateAccountWithSmsText, getOrText: getOrText$1, } = ui.authenticatorTextUtil;
3079
3147
  function SignUp() {
3080
- const { hasValidationErrors, isPending } = uiReactCore.useAuthenticator((context) => [
3148
+ const { hasValidationErrors, isPending, availableAuthMethods, preferredChallenge, selectedAuthMethod, } = uiReactCore.useAuthenticator((context) => [
3081
3149
  context.hasValidationErrors,
3082
3150
  context.isPending,
3151
+ context.availableAuthMethods,
3152
+ context.preferredChallenge,
3153
+ context.selectedAuthMethod,
3083
3154
  ]);
3084
3155
  const { handleChange, handleBlur, handleSubmit } = useFormHandlers();
3085
3156
  const { components: {
3086
3157
  // @ts-ignore
3087
3158
  SignUp: { Header = SignUp.Header, FormFields = SignUp.FormFields, Footer = SignUp.Footer, }, }, } = useCustomComponents();
3159
+ // Filter out WEB_AUTHN for sign-up (passkeys are sign-in only)
3160
+ const signUpMethods = availableAuthMethods?.filter((m) => m !== 'WEB_AUTHN');
3161
+ const hasMultipleMethods = signUpMethods && signUpMethods.length > 1;
3162
+ // Determine which method to show first
3163
+ const primaryMethod = preferredChallenge && preferredChallenge !== 'WEB_AUTHN'
3164
+ ? preferredChallenge
3165
+ : 'PASSWORD';
3166
+ // Other methods (excluding primary)
3167
+ const otherMethods = signUpMethods?.filter((m) => m !== primaryMethod) ?? [];
3168
+ // Determine if password should be shown
3169
+ const effectiveMethod = selectedAuthMethod ?? primaryMethod;
3170
+ const isPasswordless = effectiveMethod !== 'PASSWORD';
3171
+ const getButtonText = (method) => {
3172
+ switch (method) {
3173
+ case 'EMAIL_OTP':
3174
+ return getCreateAccountWithEmailText();
3175
+ case 'SMS_OTP':
3176
+ return getCreateAccountWithSmsText();
3177
+ case 'PASSWORD':
3178
+ return hasMultipleMethods
3179
+ ? getCreateAccountWithPasswordText()
3180
+ : getCreateAccountText();
3181
+ default:
3182
+ return getCreateAccountText();
3183
+ }
3184
+ };
3185
+ const handleMethodClick = (method) => (e) => {
3186
+ e.preventDefault();
3187
+ const form = e.target.closest('form');
3188
+ if (form) {
3189
+ // Get or create the hidden input
3190
+ if (!form.__authMethod) {
3191
+ const input = document.createElement('input');
3192
+ input.name = '__authMethod';
3193
+ form.appendChild(input);
3194
+ }
3195
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
3196
+ form.__authMethod.type = 'hidden';
3197
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
3198
+ form.__authMethod.value = method;
3199
+ form.requestSubmit();
3200
+ }
3201
+ };
3088
3202
  return (React__namespace["default"].createElement(Field.View, null,
3089
3203
  React__namespace["default"].createElement(Header, null),
3090
3204
  React__namespace["default"].createElement("form", { "data-amplify-form": "", "data-amplify-authenticator-signup": "", method: "post", onChange: handleChange, onSubmit: handleSubmit, onBlur: handleBlur },
3091
3205
  React__namespace["default"].createElement(FederatedSignIn, null),
3092
3206
  React__namespace["default"].createElement(Field.Flex, { as: "fieldset", direction: "column", isDisabled: isPending },
3093
3207
  React__namespace["default"].createElement(Field.Flex, { direction: "column" },
3094
- React__namespace["default"].createElement(FormFields, null),
3208
+ React__namespace["default"].createElement(FormFields, { includePassword: !isPasswordless }),
3095
3209
  React__namespace["default"].createElement(RemoteErrorMessage, null)),
3096
- React__namespace["default"].createElement(Field.Button, { isDisabled: hasValidationErrors || isPending, isFullWidth: true, type: "submit", variation: "primary", isLoading: isPending, loadingText: getCreatingAccountText() }, getCreateAccountText()),
3210
+ React__namespace["default"].createElement(Field.Button, { isDisabled: ((!hasMultipleMethods || otherMethods.length == 0) &&
3211
+ hasValidationErrors) ||
3212
+ isPending, isFullWidth: true, type: "submit", variation: "primary", isLoading: isPending, loadingText: getCreatingAccountText(), onClick: handleMethodClick(primaryMethod) }, getButtonText(primaryMethod)),
3213
+ hasMultipleMethods && otherMethods.length > 0 && (React__namespace["default"].createElement(React__namespace["default"].Fragment, null,
3214
+ React__namespace["default"].createElement(Field.Flex, { justifyContent: "center", padding: "medium 0" }, getOrText$1()),
3215
+ otherMethods.map((method) => (React__namespace["default"].createElement(Field.Button, { key: method, isDisabled: isPending, isFullWidth: true, type: "button", variation: "primary", onClick: handleMethodClick(method) }, getButtonText(method)))))),
3097
3216
  React__namespace["default"].createElement(Footer, null)))));
3098
3217
  }
3099
3218
  SignUp.Header = function Header() {
@@ -3192,28 +3311,38 @@ VerifyUser.Footer = function Footer() {
3192
3311
  return null;
3193
3312
  };
3194
3313
 
3195
- const { getChallengeText } = ui.authenticatorTextUtil;
3314
+ const { getChallengeText, getDeliveryMessageText, getDeliveryMethodText } = ui.authenticatorTextUtil;
3196
3315
  const ConfirmSignIn = ({ className, variation, }) => {
3197
- const { isPending } = uiReactCore.useAuthenticator((context) => [context.isPending]);
3316
+ const { isPending, challengeName, codeDeliveryDetails } = uiReactCore.useAuthenticator((context) => [
3317
+ context.isPending,
3318
+ context.challengeName,
3319
+ context.codeDeliveryDetails,
3320
+ ]);
3198
3321
  const { handleChange, handleSubmit } = useFormHandlers();
3199
3322
  const { components: {
3200
3323
  // @ts-ignore
3201
3324
  ConfirmSignIn: { Header = ConfirmSignIn.Header, Footer = ConfirmSignIn.Footer, }, }, } = useCustomComponents();
3325
+ const showDeliveryMessage = isOtpChallenge(challengeName) && codeDeliveryDetails;
3202
3326
  return (React__namespace["default"].createElement(RouteContainer, { className: className, variation: variation },
3203
3327
  React__namespace["default"].createElement("form", { "data-amplify-form": "", "data-amplify-authenticator-confirmsignin": "", method: "post", onChange: handleChange, onSubmit: handleSubmit },
3204
3328
  React__namespace["default"].createElement(Field.Flex, { as: "fieldset", direction: "column", isDisabled: isPending },
3205
3329
  React__namespace["default"].createElement(Header, null),
3206
3330
  React__namespace["default"].createElement(Field.Flex, { direction: "column" },
3331
+ showDeliveryMessage && (React__namespace["default"].createElement(Field.Text, { className: "amplify-authenticator__subtitle" }, getDeliveryMessageText(codeDeliveryDetails))),
3207
3332
  React__namespace["default"].createElement(FormFields, null),
3208
3333
  React__namespace["default"].createElement(RemoteErrorMessage, null)),
3209
3334
  React__namespace["default"].createElement(ConfirmSignInFooter, null),
3210
3335
  React__namespace["default"].createElement(Footer, null)))));
3211
3336
  };
3212
3337
  function Header() {
3213
- const { challengeName } = uiReactCore.useAuthenticator(({ challengeName }) => [
3338
+ const { challengeName, codeDeliveryDetails } = uiReactCore.useAuthenticator(({ challengeName, codeDeliveryDetails }) => [
3214
3339
  challengeName,
3340
+ codeDeliveryDetails,
3215
3341
  ]);
3216
- return React__namespace["default"].createElement(Heading, { level: 3 }, getChallengeText(challengeName));
3342
+ const showDeliveryMethod = isOtpChallenge(challengeName) && codeDeliveryDetails;
3343
+ return (React__namespace["default"].createElement(React__namespace["default"].Fragment, null,
3344
+ React__namespace["default"].createElement(Heading, { level: 3 }, getChallengeText(challengeName)),
3345
+ showDeliveryMethod && (React__namespace["default"].createElement(Heading, { level: 5 }, getDeliveryMethodText(codeDeliveryDetails)))));
3217
3346
  }
3218
3347
  ConfirmSignIn.Header = Header;
3219
3348
  ConfirmSignIn.Footer = function Footer() {
@@ -3247,7 +3376,7 @@ ConfirmResetPassword.Footer = function Footer() {
3247
3376
  return null;
3248
3377
  };
3249
3378
 
3250
- const { getBackToSignInText, getSendingText, getSendCodeText, getResetYourPasswordText, } = ui.authenticatorTextUtil;
3379
+ const { getBackToSignInText: getBackToSignInText$1, getSendingText, getSendCodeText, getResetYourPasswordText, } = ui.authenticatorTextUtil;
3251
3380
  const ForgotPassword = ({ className, variation, }) => {
3252
3381
  const { isPending } = uiReactCore.useAuthenticator((context) => [context.isPending]);
3253
3382
  const { handleChange, handleSubmit } = useFormHandlers();
@@ -3261,7 +3390,7 @@ const ForgotPassword = ({ className, variation, }) => {
3261
3390
  React__namespace["default"].createElement(Field.Flex, { direction: "column" },
3262
3391
  React__namespace["default"].createElement(FormFields, null)),
3263
3392
  React__namespace["default"].createElement(RemoteErrorMessage, null),
3264
- React__namespace["default"].createElement(TwoButtonSubmitFooter, { cancelButtonText: getBackToSignInText(), cancelButtonSendType: "SIGN_IN", submitButtonText: isPending ? (React__namespace["default"].createElement(React__namespace["default"].Fragment, null,
3393
+ React__namespace["default"].createElement(TwoButtonSubmitFooter, { cancelButtonText: getBackToSignInText$1(), cancelButtonSendType: "SIGN_IN", submitButtonText: isPending ? (React__namespace["default"].createElement(React__namespace["default"].Fragment, null,
3265
3394
  getSendingText(),
3266
3395
  "\u2026")) : (React__namespace["default"].createElement(React__namespace["default"].Fragment, null, getSendCodeText())) }),
3267
3396
  React__namespace["default"].createElement(Footer, null)))));
@@ -3274,8 +3403,6 @@ ForgotPassword.Footer = function Footer() {
3274
3403
  return null;
3275
3404
  };
3276
3405
 
3277
- const isSignInOrSignUpRoute = (route) => route === 'signIn' || route === 'signUp';
3278
-
3279
3406
  const { getMfaTypeLabelByValue, getSelectMfaTypeByChallengeName, getSelectMfaTypeText, } = ui.authenticatorTextUtil;
3280
3407
  const SelectMfaType = ({ className, variation, }) => {
3281
3408
  const { isPending, allowedMfaTypes = [] } = uiReactCore.useAuthenticator((context) => {
@@ -3331,6 +3458,143 @@ SetupEmail.Footer = function Footer() {
3331
3458
  return null;
3332
3459
  };
3333
3460
 
3461
+ const { getSignInWithPasswordText, getSignInWithEmailText, getSignInWithSmsText, getSignInWithPasskeyText, getSigningInText, getBackToSignInText, getOrText, getUsernameLabelByLoginMechanism, } = ui.authenticatorTextUtil;
3462
+ const getMethodButtonLabel = (method) => {
3463
+ switch (method) {
3464
+ case 'EMAIL_OTP':
3465
+ return getSignInWithEmailText();
3466
+ case 'SMS_OTP':
3467
+ return getSignInWithSmsText();
3468
+ case 'WEB_AUTHN':
3469
+ return getSignInWithPasskeyText();
3470
+ case 'PASSWORD':
3471
+ return getSignInWithPasswordText();
3472
+ default:
3473
+ return getSignInWithPasswordText();
3474
+ }
3475
+ };
3476
+ const SignInSelectAuthFactor = ({ className, variation, }) => {
3477
+ const { isPending, username, availableAuthMethods, toSignIn, selectAuthMethod, loginMechanism, } = uiReactCore.useAuthenticator((context) => [
3478
+ context.isPending,
3479
+ context.username,
3480
+ context.availableAuthMethods,
3481
+ context.toSignIn,
3482
+ context.selectAuthMethod,
3483
+ context.loginMechanism,
3484
+ ]);
3485
+ const { handleChange, handleSubmit } = useFormHandlers();
3486
+ const methods = (availableAuthMethods ?? []);
3487
+ const hasPassword = methods.includes('PASSWORD');
3488
+ const passwordlessMethods = methods.filter((m) => m !== 'PASSWORD');
3489
+ const handleMethodSelect = (method) => {
3490
+ selectAuthMethod({ method });
3491
+ };
3492
+ const usernameLabel = getUsernameLabelByLoginMechanism(loginMechanism);
3493
+ return (React__namespace["default"].createElement(RouteContainer, { className: className, variation: variation },
3494
+ React__namespace["default"].createElement("form", { "data-amplify-form": true, "data-amplify-authenticator-signinselect": true, method: "post", onSubmit: handleSubmit, onChange: handleChange },
3495
+ React__namespace["default"].createElement(Field.Flex, { direction: "column" },
3496
+ React__namespace["default"].createElement(TextField, { label: usernameLabel, name: "username", value: username, isReadOnly: true, isDisabled: true }),
3497
+ hasPassword && (React__namespace["default"].createElement(React__namespace["default"].Fragment, null,
3498
+ React__namespace["default"].createElement(PasswordField, { label: "Password", name: "password", autoComplete: "current-password", isDisabled: isPending }),
3499
+ React__namespace["default"].createElement(Field.Button, { isDisabled: isPending, type: "submit", variation: "primary", isFullWidth: true, isLoading: isPending, loadingText: getSigningInText() }, getSignInWithPasswordText()))),
3500
+ hasPassword && passwordlessMethods.length > 0 && (React__namespace["default"].createElement(Divider, { label: getOrText() })),
3501
+ passwordlessMethods.length > 0 && (React__namespace["default"].createElement(Field.Flex, { direction: "column", gap: "0.5rem" }, passwordlessMethods.map((method) => (React__namespace["default"].createElement(Field.Button, { key: method, variation: "primary", isFullWidth: true, isDisabled: isPending, onClick: () => handleMethodSelect(method) }, getMethodButtonLabel(method)))))),
3502
+ React__namespace["default"].createElement(RemoteErrorMessage, null),
3503
+ React__namespace["default"].createElement(Field.Button, { onClick: toSignIn, size: "small", variation: "link", isDisabled: isPending }, getBackToSignInText())))));
3504
+ };
3505
+
3506
+ const { getPasskeyPromptHeadingText, getPasskeyPromptDescriptionText, getCreatePasskeyText, getRegisteringText, getContinueWithoutPasskeyText, getPasskeyCreatedSuccessText, getPasskeyRegisteredText, getPasskeyRegistrationFailedText, getPasskeyLabelText, getExistingPasskeysText, getSetupAnotherPasskeyText, getContinueText, } = ui.authenticatorTextUtil;
3507
+ function PasskeyPrompt({ className, variation, }) {
3508
+ const [success, setSuccess] = React__namespace["default"].useState(false);
3509
+ const [error, setError] = React__namespace["default"].useState(null);
3510
+ const [isRegistering, setIsRegistering] = React__namespace["default"].useState(false);
3511
+ const [credentials, setCredentials] = React__namespace["default"].useState([]);
3512
+ const { submitForm, isPending } = uiReactCore.useAuthenticator((context) => [
3513
+ context.submitForm,
3514
+ context.isPending,
3515
+ ]);
3516
+ const loadCredentials = React__namespace["default"].useCallback(async () => {
3517
+ try {
3518
+ const result = await auth.listWebAuthnCredentials();
3519
+ setCredentials(result.credentials || []);
3520
+ }
3521
+ catch {
3522
+ // Silently fail - credentials list is optional
3523
+ }
3524
+ }, []);
3525
+ React__namespace["default"].useEffect(() => {
3526
+ if (success) {
3527
+ void loadCredentials();
3528
+ }
3529
+ }, [success, loadCredentials]);
3530
+ const handleSkip = React__namespace["default"].useCallback(() => {
3531
+ submitForm({ type: 'SKIP' });
3532
+ }, [submitForm]);
3533
+ const handleContinue = React__namespace["default"].useCallback(() => {
3534
+ submitForm({ type: 'SUBMIT' });
3535
+ }, [submitForm]);
3536
+ const handleRegister = React__namespace["default"].useCallback(async () => {
3537
+ try {
3538
+ setError(null);
3539
+ setIsRegistering(true);
3540
+ await auth.associateWebAuthnCredential();
3541
+ setSuccess(true);
3542
+ }
3543
+ catch (err) {
3544
+ const error = err;
3545
+ const errorName = error.name ?? '';
3546
+ const errorMessage = error.message ?? '';
3547
+ // If user canceled, skip silently without showing error
3548
+ if (errorName === 'PasskeyRegistrationCanceled' ||
3549
+ errorName === 'NotAllowedError' ||
3550
+ errorMessage.includes('canceled') ||
3551
+ errorMessage.includes('cancelled') ||
3552
+ errorMessage.includes('ceremony has been canceled') ||
3553
+ errorMessage.includes('not allowed')) {
3554
+ handleSkip();
3555
+ return;
3556
+ }
3557
+ // Show user-friendly error message
3558
+ setError(getPasskeyRegistrationFailedText());
3559
+ }
3560
+ finally {
3561
+ setIsRegistering(false);
3562
+ }
3563
+ }, [handleSkip]);
3564
+ if (success) {
3565
+ return (React__namespace["default"].createElement(RouteContainer, { className: className, variation: variation },
3566
+ React__namespace["default"].createElement(Field.Flex, { direction: "column", padding: "large", "data-amplify-authenticator-passkeyprompt": "" },
3567
+ React__namespace["default"].createElement(Field.Flex, { direction: "column", gap: "xs" },
3568
+ React__namespace["default"].createElement(Field.IconCheckCircleFill, { className: "amplify-authenticator__passkey-success-icon" }),
3569
+ React__namespace["default"].createElement(Heading, { level: 4 }, getPasskeyCreatedSuccessText()),
3570
+ React__namespace["default"].createElement(Field.Text, null, getPasskeyRegisteredText())),
3571
+ credentials.length > 0 && (React__namespace["default"].createElement(Field.View, { marginTop: "large" },
3572
+ React__namespace["default"].createElement(Heading, { level: 5 }, getExistingPasskeysText()),
3573
+ React__namespace["default"].createElement(Field.Flex, { direction: "column", gap: "xs", marginTop: "xs" }, credentials.map((cred, index) => (React__namespace["default"].createElement(Field.View, { key: cred.credentialId, className: "amplify-authenticator__passkey-credential-item" },
3574
+ React__namespace["default"].createElement(Field.Text, { fontSize: "small" }, cred.friendlyCredentialName ??
3575
+ `${getPasskeyLabelText()} ${index + 1}`))))))),
3576
+ React__namespace["default"].createElement(Field.Flex, { direction: "column", gap: "medium", marginTop: "large" },
3577
+ React__namespace["default"].createElement(Field.Button, { onClick: () => {
3578
+ setSuccess(false);
3579
+ handleRegister();
3580
+ }, variation: "link" }, getSetupAnotherPasskeyText()),
3581
+ React__namespace["default"].createElement(Field.Button, { onClick: () => handleContinue(), variation: "primary", isFullWidth: true }, getContinueText())))));
3582
+ }
3583
+ const isButtonDisabled = isPending || isRegistering;
3584
+ return (React__namespace["default"].createElement(RouteContainer, { className: className, variation: variation },
3585
+ React__namespace["default"].createElement("form", { "data-amplify-form": "", "data-amplify-authenticator-passkeyprompt": "" },
3586
+ React__namespace["default"].createElement(Field.Flex, { direction: "column", gap: "medium" },
3587
+ React__namespace["default"].createElement(Heading, { level: 4 }, getPasskeyPromptHeadingText()),
3588
+ React__namespace["default"].createElement(Field.Text, null, getPasskeyPromptDescriptionText()),
3589
+ React__namespace["default"].createElement(Field.Flex, { justifyContent: "center" },
3590
+ React__namespace["default"].createElement(Field.IconPasskey, { className: "amplify-authenticator__passkey-icon" })),
3591
+ React__namespace["default"].createElement(Field.Flex, { direction: "column", gap: "medium" },
3592
+ React__namespace["default"].createElement(Field.Button, { onClick: () => void handleRegister(), variation: "primary", isDisabled: isButtonDisabled, isLoading: isRegistering, loadingText: getRegisteringText() }, getCreatePasskeyText()),
3593
+ React__namespace["default"].createElement(Field.Button, { onClick: handleSkip, variation: "link", isDisabled: isButtonDisabled }, getContinueWithoutPasskeyText()),
3594
+ error && (React__namespace["default"].createElement(Field.Text, { className: "amplify-authenticator__passkey-error" }, error)),
3595
+ React__namespace["default"].createElement(RemoteErrorMessage, null))))));
3596
+ }
3597
+
3334
3598
  function RenderNothing() {
3335
3599
  // @ts-ignore
3336
3600
  return null;
@@ -3355,6 +3619,10 @@ const getRouteComponent = (route) => {
3355
3619
  case 'signIn':
3356
3620
  case 'signUp':
3357
3621
  return SignInSignUpTabs;
3622
+ case 'signInSelectAuthFactor':
3623
+ return SignInSelectAuthFactor;
3624
+ case 'passkeyPrompt':
3625
+ return PasskeyPrompt;
3358
3626
  case 'forceNewPassword':
3359
3627
  return ForceNewPassword;
3360
3628
  case 'forgotPassword':
@@ -3441,7 +3709,7 @@ const defaultComponents = {
3441
3709
  // which allows the `Authenticator` to just return `children` if a user is authenticated.
3442
3710
  // Once the `Provider` is removed from the `Authenticator` component and exported as
3443
3711
  // `AuthenticatorProvider`, this component should be renamed to `Authenticator`.
3444
- function AuthenticatorInternal({ children, className, components: customComponents, formFields, hideSignUp, initialState, loginMechanisms, passwordSettings, signUpAttributes, services, socialProviders, variation, }) {
3712
+ function AuthenticatorInternal({ children, className, components: customComponents, formFields, hideSignUp, initialState, loginMechanisms, passwordSettings, passwordless, signUpAttributes, services, socialProviders, variation, }) {
3445
3713
  Field.useDeprecationWarning({
3446
3714
  message: 'The `passwordSettings` prop has been deprecated and will be removed in a future major version of Amplify UI.',
3447
3715
  // shouldWarn: !!passwordSettings,
@@ -3455,6 +3723,7 @@ function AuthenticatorInternal({ children, className, components: customComponen
3455
3723
  initialState,
3456
3724
  loginMechanisms,
3457
3725
  passwordSettings,
3726
+ passwordless,
3458
3727
  services,
3459
3728
  signUpAttributes,
3460
3729
  socialProviders,
package/dist/internal.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var Field = require('./Field-BjInyX0G.js');
5
+ var Field = require('./Field-Cq088Vbv.js');
6
6
  var React = require('react');
7
7
  var Storage = require('aws-amplify/storage');
8
8
  var ui = require('@aws-amplify/ui');
@@ -17129,6 +17129,7 @@ exports.FileSelect = Field.FileSelect;
17129
17129
  exports.IconAdd = Field.IconAdd;
17130
17130
  exports.IconCheck = Field.IconCheck;
17131
17131
  exports.IconCheckCircle = Field.IconCheckCircle;
17132
+ exports.IconCheckCircleFill = Field.IconCheckCircleFill;
17132
17133
  exports.IconChevronLeft = Field.IconChevronLeft;
17133
17134
  exports.IconChevronRight = Field.IconChevronRight;
17134
17135
  exports.IconClose = Field.IconClose;
@@ -17137,6 +17138,7 @@ exports.IconExpandMore = Field.IconExpandMore;
17137
17138
  exports.IconIndeterminate = Field.IconIndeterminate;
17138
17139
  exports.IconInfo = Field.IconInfo;
17139
17140
  exports.IconMenu = Field.IconMenu;
17141
+ exports.IconPasskey = Field.IconPasskey;
17140
17142
  exports.IconRemove = Field.IconRemove;
17141
17143
  exports.IconSearch = Field.IconSearch;
17142
17144
  exports.IconStar = Field.IconStar;
@@ -384,7 +384,6 @@
384
384
  animation-timing-function: linear;
385
385
  animation-name: spin;
386
386
  }
387
-
388
387
  @keyframes spin {
389
388
  0% {
390
389
  transform: rotate(0deg);
@@ -385,7 +385,6 @@
385
385
  animation-timing-function: linear;
386
386
  animation-name: spin;
387
387
  }
388
-
389
388
  @keyframes spin {
390
389
  0% {
391
390
  transform: rotate(0deg);
@@ -69,4 +69,21 @@
69
69
  .amplify-authenticator__federated-button {
70
70
  font-weight: normal;
71
71
  gap: var(--amplify-space-medium);
72
+ }
73
+
74
+ [data-amplify-authenticator-passkeyprompt] .amplify-authenticator__passkey-success-icon {
75
+ font-size: var(--amplify-font-sizes-xxxl);
76
+ color: var(--amplify-colors-green-60, #34a853);
77
+ }
78
+ [data-amplify-authenticator-passkeyprompt] .amplify-authenticator__passkey-credential-item {
79
+ padding: var(--amplify-space-medium);
80
+ background-color: var(--amplify-colors-background-secondary);
81
+ border-radius: var(--amplify-radii-small);
82
+ }
83
+ [data-amplify-authenticator-passkeyprompt] .amplify-authenticator__passkey-error {
84
+ color: var(--amplify-colors-font-error);
85
+ margin-top: var(--amplify-space-small);
86
+ }
87
+ [data-amplify-authenticator-passkeyprompt] .amplify-authenticator__passkey-icon {
88
+ font-size: var(--amplify-components-authenticator-passkey-icon-size, 12rem);
72
89
  }
@@ -71,4 +71,21 @@
71
71
  font-weight: normal;
72
72
  gap: var(--amplify-space-medium);
73
73
  }
74
+
75
+ [data-amplify-authenticator-passkeyprompt] .amplify-authenticator__passkey-success-icon {
76
+ font-size: var(--amplify-font-sizes-xxxl);
77
+ color: var(--amplify-colors-green-60, #34a853);
78
+ }
79
+ [data-amplify-authenticator-passkeyprompt] .amplify-authenticator__passkey-credential-item {
80
+ padding: var(--amplify-space-medium);
81
+ background-color: var(--amplify-colors-background-secondary);
82
+ border-radius: var(--amplify-radii-small);
83
+ }
84
+ [data-amplify-authenticator-passkeyprompt] .amplify-authenticator__passkey-error {
85
+ color: var(--amplify-colors-font-error);
86
+ margin-top: var(--amplify-space-small);
87
+ }
88
+ [data-amplify-authenticator-passkeyprompt] .amplify-authenticator__passkey-icon {
89
+ font-size: var(--amplify-components-authenticator-passkey-icon-size, 12rem);
90
+ }
74
91
  }