diff --git a/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-in/index.mdx b/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-in/index.mdx index 590daa686f4..78632d46b48 100644 --- a/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-in/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-in/index.mdx @@ -259,8 +259,10 @@ The `signIn` API response will include a `nextStep` property, which can be used | `CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED` | The user was created with a temporary password and must set a new one. Complete the process with `confirmSignIn`. | | `CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE` | The sign-in must be confirmed with a custom challenge response. Complete the process with `confirmSignIn`. | | `CONFIRM_SIGN_IN_WITH_TOTP_CODE` | The sign-in must be confirmed with a TOTP code from the user. Complete the process with `confirmSignIn`. | -| `CONFIRM_SIGN_IN_WITH_SMS_CODE` | The sign-in must be confirmed with a SMS code from the user. Complete the process with `confirmSignIn`. | -| `CONFIRM_SIGN_IN_WITH_EMAIL_CODE` | The sign-in must be confirmed with a EMAIL code from the user. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_SMS_CODE` | The sign-in must be confirmed with an SMS code from the user. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_EMAIL_CODE` | The sign-in must be confirmed with an EMAIL code from the user. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_PASSWORD` | The sign-in must be confirmed with the password from the user. Complete the process with `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION` | The user must select their mode of first factor authentication. Complete the process by passing the desired mode to the `challengeResponse` field of `confirmSignIn`. | | `CONTINUE_SIGN_IN_WITH_MFA_SELECTION` | The user must select their mode of MFA verification before signing in. Complete the process with `confirmSignIn`. | | `CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION` | The user must select their mode of MFA verification to setup. Complete the process by passing either `"EMAIL"` or `"TOTP"` to `confirmSignIn`. | | `CONTINUE_SIGN_IN_WITH_TOTP_SETUP` | The TOTP setup process must be continued. Complete the process with `confirmSignIn`. | @@ -591,6 +593,8 @@ Following sign in, you will receive a `nextStep` in the sign-in result of one of | `CONFIRM_SIGN_IN_WITH_TOTP_CODE` | The sign-in must be confirmed with a TOTP code from the user. Complete the process with `confirmSignIn`. | | `CONFIRM_SIGN_IN_WITH_SMS_CODE` | The sign-in must be confirmed with a SMS code from the user. Complete the process with `confirmSignIn`. | | `CONFIRM_SIGN_IN_WITH_EMAIL_CODE` | The sign-in must be confirmed with a EMAIL code from the user. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_PASSWORD` | The sign-in must be confirmed with the password from the user. Complete the process with `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION` | The user must select their mode of first factor authentication. Complete the process by passing the desired mode to the `challengeResponse` field of `confirmSignIn`. | | `CONTINUE_SIGN_IN_WITH_MFA_SELECTION` | The user must select their mode of MFA verification before signing in. Complete the process with `confirmSignIn`. | | `CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION` | The user must select their mode of MFA verification to setup. Complete the process by passing either `"EMAIL"` or `"TOTP"` to `confirmSignIn`. | | `CONTINUE_SIGN_IN_WITH_TOTP_SETUP` | The TOTP setup process must be continued. Complete the process with `confirmSignIn`. | @@ -1113,7 +1117,18 @@ Your application's users can also sign in using passwordless methods. To learn m -{/* */} +To request an OTP code via SMS for authentication, the challenge is passed as the challenge response to the confirm sign in API. + +Amplify will respond appropriately to Cognito and return the challenge as sign in next step: `CONFIRM_SIGN_IN_WITH_SMS_CODE`: + +```ts +const { nextStep } = await confirmSignIn({ + challengeResponse: "SMS_OTP" +}); + +// nextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_SMS_CODE' +handleNextSignInStep(nextStep); +``` @@ -1205,7 +1220,18 @@ func confirmSignIn() -> AnyCancellable { -{/* */} +To request an OTP code via email for authentication, the challenge is passed as the challenge response to the confirm sign in API. + +Amplify will respond appropriately to Cognito and return the challenge as sign in next step: `CONFIRM_SIGN_IN_WITH_EMAIL_CODE`: + +```ts +const { nextStep } = await confirmSignIn({ + challengeResponse: "EMAIL_OTP" +}); + +// nextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_EMAIL_CODE' +handleNextSignInStep(nextStep); +``` @@ -1290,3 +1316,62 @@ func confirmSignIn() -> AnyCancellable { + +### WebAuthn Passkeys + +{/* blurb with supplemental information about handling sign-in, events, etc. */} + + + +The WebAuthn credential flow is initiated by passing the challenge name to the confirm sign in api. + +```ts +const { nextStep } = await confirmSignIn({ + challengeResponse: "WEB_AUTHN", +}); + +// nextStep.signInStep === 'DONE' +handleNextSignInStep(nextStep); +``` + + + + +{/* */} + + + + +{/* */} + + + + +{/* */} + + + + + +### Password or SRP + +Traditional password based authentication is available from this flow as well. To initiate this flow from select challenge, either `PASSWORD` or `PASSWORD_SRP` is passed as the challenge response. + +```ts +const { nextStep } = await confirmSignIn({ + challengeResponse: "PASSWORD_SRP", // or "PASSWORD" +}); + +// in both cases +// nextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_PASSWORD' +handleNextSignInStep(nextStep); + +const { nextStep: nextNextStep } = await confirmSignIn({ + challengeResponse: "Test123#", +}); + +// nextNextStep.signInStep === 'DONE' +handleNextSignInStep(nextNextStep); +``` + + diff --git a/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-up/index.mdx b/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-up/index.mdx index bea418c83c4..61791f1b5e4 100644 --- a/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-up/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-up/index.mdx @@ -538,7 +538,40 @@ Your application's users can also sign up using passwordless methods. To learn m -{/* */} +```typescript +// Sign up using a phone number +const { nextStep: signUpNextStep } = await signUp({ + username: 'james', + options: { + userAttributes: { + phone_number: '+15555551234', + }, + }, +}); + +if (signUpNextStep.signUpStep === 'DONE') { + console.log(`SignUp Complete`); +} + +if (signUpNextStep.signUpStep === 'CONFIRM_SIGN_UP') { + console.log( + `Code Delivery Medium: ${signUpNextStep.codeDeliveryDetails.deliveryMedium}`, + ); + console.log( + `Code Delivery Destination: ${signUpNextStep.codeDeliveryDetails.destination}`, + ); +} + +// Confirm sign up with the OTP received +const { nextStep: confirmSignUpNextStep } = await confirmSignUp({ + username: 'james', + confirmationCode: '123456', +}); + +if (confirmSignUpNextStep.signUpStep === 'DONE') { + console.log(`SignUp Complete`); +} +``` @@ -653,7 +686,40 @@ func confirmSignUp(for username: String, with confirmationCode: String) -> AnyCa -{/* */} +```typescript +// Sign up using an email address +const { nextStep: signUpNextStep } = await signUp({ + username: 'james', + options: { + userAttributes: { + email: 'james@example.com', + }, + }, +}); + +if (signUpNextStep.signUpStep === 'DONE') { + console.log(`SignUp Complete`); +} + +if (signUpNextStep.signUpStep === 'CONFIRM_SIGN_UP') { + console.log( + `Code Delivery Medium: ${signUpNextStep.codeDeliveryDetails.deliveryMedium}`, + ); + console.log( + `Code Delivery Destination: ${signUpNextStep.codeDeliveryDetails.destination}`, + ); +} + +// Confirm sign up with the OTP received +const { nextStep: confirmSignUpNextStep } = await confirmSignUp({ + username: 'james', + confirmationCode: '123456', +}); + +if (confirmSignUpNextStep.signUpStep === 'DONE') { + console.log(`SignUp Complete`); +} +``` @@ -768,8 +834,21 @@ func confirmSignUp(for username: String, with confirmationCode: String) -> AnyCa -{/* */} +```typescript +// Confirm sign up with the OTP received and auto sign in +const { nextStep: confirmSignUpNextStep } = await confirmSignUp({ + username: 'james', + confirmationCode: '123456', +}); +if (confirmSignUpNextStep.signUpStep === 'COMPLETE_AUTO_SIGN_IN') { + const { nextStep } = await autoSignIn(); + + if (nextStep.signInStep === 'DONE') { + console.log('Successfully signed in.'); + } +} +``` diff --git a/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/switching-authentication-flows/index.mdx b/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/switching-authentication-flows/index.mdx index 6908651272b..e5cdad0d7b2 100644 --- a/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/switching-authentication-flows/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/switching-authentication-flows/index.mdx @@ -145,28 +145,84 @@ Follow the instructions in [Custom Auth Sign In](/gen1/[platform]/build-a-backen -For client side authentication there are three different flows: +For client side authentication there are four different flows: -1. `USER_SRP_AUTH`: The `USER_SRP_AUTH` flow uses the [SRP protocol (Secure Remote Password)](https://en.wikipedia.org/wiki/Secure_Remote_Password_protocol) where the password never leaves the client and is unknown to the server. This is the recommended flow and is used by default. +1. `USER_AUTH`: The `USER_AUTH` flow is designed to be flexible and supports both password and passwordless sign in factors. `USER_AUTH` can do username-password and SRP authentication without the other flows being configured. This flow doesn't include CUSTOM_AUTH. This is the recommended flow. -2. `USER_PASSWORD_AUTH`: The `USER_PASSWORD_AUTH` flow will send user credentials to the backend without applying SRP encryption. If you want to migrate users to Cognito using the "Migration" trigger and avoid forcing users to reset their passwords, you will need to use this authentication type because the Lambda function invoked by the trigger needs to verify the supplied credentials. +2. `USER_SRP_AUTH`: The `USER_SRP_AUTH` flow uses the [SRP protocol (Secure Remote Password)](https://en.wikipedia.org/wiki/Secure_Remote_Password_protocol) where the password never leaves the client and is unknown to the server. -3. `CUSTOM_WITH_SRP` & `CUSTOM_WITHOUT_SRP`: Allows for a series of challenge and response cycles that can be customized to meet different requirements. +3. `USER_PASSWORD_AUTH`: The `USER_PASSWORD_AUTH` flow will send user credentials unencrypted to the backend. If you want to migrate users to Cognito using the "Migration" trigger and avoid forcing users to reset their passwords, you will need to use this authentication type because the Lambda function invoked by the trigger needs to verify the supplied credentials. + +4. `CUSTOM_WITH_SRP` & `CUSTOM_WITHOUT_SRP`: Allows for a series of challenge and response cycles that can be customized to meet different requirements. The Auth flow can be customized when calling `signIn`, for example: ```ts title="src/main.ts" await signIn({ - username: "hello@mycompany.com", + username: "hello@mycompany.com", password: "hunter2", options: { - authFlowType: 'USER_PASSWORD_AUTH' + authFlowType: 'USER_AUTH' } }) ``` > For more information about authentication flows, please visit [AWS Cognito developer documentation](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html#amazon-cognito-user-pools-custom-authentication-flow) +## USER_AUTH flow + +In order to facilitate the new passwordless sign in options, Cognito is introducing a new auth flow type known as `USER_AUTH`. This flow is designed to be flexible and supports both password and passwordless sign in factors. + +### Set up auth backend + +Configure supported flows in your Cognito app client. In the AWS Console, this is done by ticking the checkboxes at General settings > App clients > App client information (Edit) > App client flows. If you're using the AWS CLI or CloudFormation, update your app client by adding `USER_AUTH` to the list of "Explicit Auth Flows". + +### User authentication flow + +The `USER_AUTH` sign in flow will support the following methods of first factor authentication: `WEB_AUTHN`, `EMAIL_OTP`, `SMS_OTP`, `PASSWORD`, and `PASSWORD_SRP`. + +```ts +type AuthFactorType = + | "WEB_AUTHN" + | "EMAIL_OTP" + | "SMS_OTP" + | "PASSWORD" + | "PASSWORD_SRP"; +``` + +If the desired first factor is known before the sign in flow is initiated it can be passed to the initial sign in call. + +Password flows will require the password to be passed in the same step. Passwordless flows do not require user input, and the challenges will be handled in the next sign in step: + +```ts +// PASSWORD_SRP / PASSWORD +// sign in with preferred challenge as password +// note password must be provided in same step +const { nextStep } = await signIn({ + username: "hello@mycompany.com", + password: "hunter2", + options: { + authFlowType: "USER_AUTH", + preferredChallenge: "PASSWORD_SRP" // or "PASSWORD" + }, +}); + +// WEB_AUTHN / EMAIL_OTP / SMS_OTP +// sign in with preferred passwordless challenge +// no user input required at this step +const { nextStep } = await signIn({ + username: "passwordless@mycompany.com", + options: { + authFlowType: "USER_AUTH", + preferredChallenge: "WEB_AUTHN" // or "EMAIL_OTP" or "SMS_OTP" + }, +}); +``` + +If the desired first factor is not known, the flow will continue to select an available first factor. + +> For more information about determining a first factor, and signing in with passwordless authorization factors, please visit the [concepts page for passwordless](/[platform]/build-a-backend/auth/concepts/passwordless/) + ## USER_PASSWORD_AUTH flow A use case for the `USER_PASSWORD_AUTH` authentication flow is migrating users into Amazon Cognito diff --git a/src/pages/[platform]/build-a-backend/auth/manage-users/manage-webauthn-credentials/index.mdx b/src/pages/[platform]/build-a-backend/auth/manage-users/manage-webauthn-credentials/index.mdx index 3b22e120501..6438c6ab098 100644 --- a/src/pages/[platform]/build-a-backend/auth/manage-users/manage-webauthn-credentials/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/manage-users/manage-webauthn-credentials/index.mdx @@ -27,11 +27,29 @@ export function getStaticProps() { }; } + + + +WebAuthn registration and authentication are not currently supported on React Native, other passwordless features are fully supported. + + + + +Amplify Auth enables your users to associate, keep track of, and delete passkeys. + ## Associate WebAuthN credentials + +Note that users must be authenticated to register a passkey. That also means users cannot create a passkey during sign up; consequently, they must have at least one other first factor authentication mechanism associated with their account to use WebAuthn. + +You can associate a passkey using the following API: -{/* */} +```ts +import { associateWebAuthnCredential} from 'aws-amplify/auth'; + +await associateWebAuthnCredential(); +``` @@ -75,12 +93,45 @@ func associateWebAuthNCredentials() -> AnyCancellable { + +You will be prompted to register a passkey using your local authenticator. Amplify will then associate that passkey with Cognito. ## List WebAuthN credentials +You can list registered passkeys using the following API: + -{/* */} +```ts +import { listWebAuthnCredentials } from 'aws-amplify/auth'; + +await listWebAuthnCredentials(); +``` + +The list passkeys API takes optional parameters `pageSize` and `nextToken` which may be used to paginate the list request. + +The API returns a list of `AuthWebAuthnCredential` objects, which define the properties of a passkey, as well as a `nextToken` to be used in subsequent list requests. + +```ts +interface ListWebAuthnCredentialInput { + pageSize?: number; + nextToken?: string; +} + +interface ListWebAuthnCredentialOutput { + credentials: AuthWebAuthnCredential[]; + nextToken?: string; +} + +interface AuthWebAuthnCredential { + credentialId: string | undefined; + friendlyCredentialName: string | undefined; + relyingPartyId: string | undefined; + authenticatorAttachment?: string; + authenticatorTransports: string[] | undefined; + createdAt: Date | undefined; +} +``` @@ -156,9 +207,27 @@ func listWebAuthNCredentials() -> AnyCancellable { ## Delete WebAuthN credentials +You can delete a passkey with the following API: + -{/* */} +```ts +import { deleteWebAuthnCredential } from 'aws-amplify/auth'; + +const id = "credential-id-to-delete"; + +await deleteWebAuthnCredential({ + credentialId: id +}); +``` + +The delete passkey API has only the required `credentialId` as input. + +```ts +interface DeleteWebAuthnCredentialInput { + credentialId: string; +} +``` @@ -200,5 +269,40 @@ func deleteWebAuthNCredentials(credentialId: String) -> AnyCancellable { + + + +## Practical example + + +Here is a code example that uses the list and delete APIs together. In this example, the user has 3 passkeys registered. They want to list all passkeys while using a `pageSize` of 2 as well as delete the first passkey in the list. + +```ts +import { + listWebAuthnCredentials, + deleteWebAuthnCredential +} from 'aws-amplify/auth'; + +let passkeys = []; + +const result = await listWebAuthnCredentials({ pageSize: 2 }); + +passkeys.push(...result.credentials); + +const nextPage = await listWebAuthnCredentials({ + pageSize: 2, + nextToken: result.nextToken, +}); + +passkeys.push(...nextPage.credentials); + +const id = passkeys[0].credentialId; + +await deleteWebAuthnCredential({ + credentialId: id +}); +``` + +