@aws-amplify/ui
Version:
`@aws-amplify/ui` contains low-level logic & styles for stand-alone usage or re-use in framework-specific implementations.
132 lines (129 loc) • 5.56 kB
JavaScript
import pickBy from 'lodash/pickBy.js';
import '@aws-amplify/core/internals/utils';
import '../../utils/setUserAgent/constants.mjs';
import { isString } from '../../utils/utils.mjs';
// default `autoSignIn` flag is `true`
const DEFAULT_AUTO_SIGN_IN = true;
const EMPTY_STRING = '';
const sanitizePhoneNumber = (dialCode, phoneNumber) => `${dialCode}${phoneNumber}`.replace(/[^A-Z0-9+]/gi, '');
const selectUserAttributes = (_, key) => {
// Allowlist of Cognito User Pool Attributes (from OpenID Connect specification)
// See: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html
switch (key) {
case 'address':
case 'birthdate':
case 'email':
case 'family_name':
case 'gender':
case 'given_name':
case 'locale':
case 'middle_name':
case 'name':
case 'nickname':
case 'phone_number':
case 'picture':
case 'preferred_username':
case 'profile':
case 'updated_at':
case 'website':
case 'zoneinfo':
return true;
// Otherwise, it's a custom attribute
default:
return key.startsWith('custom:');
}
};
const getUserAttributes = (formValues) => {
const { phone_number, ...userAttributes } = pickBy(formValues, selectUserAttributes);
// only include `phone_number` attribute in `userAttributes` if it has a value
if (isString(phone_number) && phone_number !== EMPTY_STRING) {
const { country_code } = formValues;
return {
...userAttributes,
phone_number: sanitizePhoneNumber(country_code, phone_number),
};
}
return userAttributes;
};
const getSignUpInput = (username, formValues, loginMechanism, authMethod) => {
const { password, ...values } = formValues;
const attributes = getUserAttributes(values);
const isPasswordless = authMethod && authMethod !== 'PASSWORD';
// For SMS OTP, set the email channel to empty string if present to force account creation with phone number
let userAttributes = attributes;
if (authMethod === 'SMS_OTP' && attributes.email) {
userAttributes = { ...attributes, email: '' };
}
const options = {
autoSignIn: isPasswordless
? {
enabled: true,
authFlowType: 'USER_AUTH',
preferredChallenge: authMethod,
}
: DEFAULT_AUTO_SIGN_IN,
userAttributes: {
// use `username` value for `phone_number`
...(loginMechanism === 'phone_number'
? { ...userAttributes, phone_number: username }
: userAttributes),
},
};
return { username, password: isPasswordless ? undefined : password, options };
};
const getUsernameSignUp = ({ formValues, loginMechanisms, }) => {
// Check if a specific auth method was selected via form data
const authMethod = formValues.__authMethod;
// For SMS_OTP, always use phone_number as username
if (authMethod === 'SMS_OTP') {
const { country_code, phone_number } = formValues;
return sanitizePhoneNumber(country_code, phone_number);
}
// For EMAIL_OTP, always use email as username
if (authMethod === 'EMAIL_OTP') {
return formValues.email;
}
// When 'username' is in loginMechanisms, always use the username field for the Username parameter.
// This handles both username-only mode and alias mode (username + email/phone as sign-in options).
// See: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html#user-pool-settings-aliases
if (loginMechanisms.includes('username')) {
return formValues.username;
}
const loginMechanism = loginMechanisms[0];
if (loginMechanism === 'phone_number') {
const { country_code, phone_number } = formValues;
return sanitizePhoneNumber(country_code, phone_number);
}
// Otherwise, use the primary login mechanism (email as username)
return formValues[loginMechanism];
};
/**
* Get available authentication methods based on backend capabilities and hidden methods
*/
const getAvailableAuthMethods = (passwordlessCapabilities, hiddenAuthMethods) => {
const allMethods = [];
// If hiddenAuthMethods is explicitly provided (even as empty array),
// assume all methods are available and let hiddenAuthMethods filter them
const assumeAllAvailable = hiddenAuthMethods !== undefined;
// PASSWORD is always available by default
allMethods.push('PASSWORD');
// Add passwordless methods if backend supports them OR if hiddenAuthMethods is explicitly set
if (assumeAllAvailable || passwordlessCapabilities?.emailOtpEnabled) {
allMethods.push('EMAIL_OTP');
}
if (assumeAllAvailable || passwordlessCapabilities?.smsOtpEnabled) {
allMethods.push('SMS_OTP');
}
if (assumeAllAvailable || passwordlessCapabilities?.webAuthnEnabled) {
allMethods.push('WEB_AUTHN');
}
// Filter out hidden methods
const hidden = hiddenAuthMethods ?? [];
const availableMethods = allMethods.filter((method) => !hidden.includes(method));
// Validate that at least one method remains
if (availableMethods.length === 0) {
throw new Error('InvalidPasswordlessSettings: All authentication methods are hidden');
}
return availableMethods;
};
export { getAvailableAuthMethods, getSignUpInput, getUserAttributes, getUsernameSignUp, sanitizePhoneNumber };