DEV Community

Arpad Toth for AWS Community Builders

Posted on • Originally published at arpadt.com

Implementing passwordless sign-in flow with text messages in Cognito

We can use Cognito user pools to create passwordless user authentication flows for our applications. One option is to receive a verification code in a text message.

1. Problem statement

Alice has been assigned a new project involving a business need for passwordless authentication in one of the company's applications. Some managers believe passwords are outdated and less secure compared to passwordless options.

The application currently uses a Cognito user pool for user authentication. At present, users sign in using their username and password. However, it is now a requirement to provide new users with the option of passwordless authentication by receiving a verification code via SMS.

Alice's task is to research the SMS passwordless sign-in option in Cognito and present her findings to management.

2. Pre-requisites

This post will not cover how to create the following:

  • A Cognito user pool with hosted UI/managed login, Cognito domain, and callback URL.
  • An app client.
  • An IAM role.

Additionally, it does not detail how to send SMS messages using SNS.

Links to the relevant documentation pages will be provided at the end of the post for those who need them.

To enable passwordless authentication, the Cognito user pool must be on either the Essentials (default for new user pools) or the Plus feature plan.

3. Passwordless Authentication with SMS

Here's how we can configure Cognito to send text messages to users' phones.

3.1. Configure SNS for Cognito

Cognito relies on Simple Notification Service (SNS) to send SMS messages. The first step is to connect SNS with Cognito.

Since every API call in AWS requires permissions, even for interactions between services, we need to configure these permissions in AWS Identity and Access Management (IAM).

The user pool will assume a role to interact with SNS. Fortunately, Cognito can create this role automatically. In the user pool's menu, under Authentication methods, select SMS and choose the Create a new IAM role option.

The required permissions policy must allow the sns:Publish action:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "sns:publish"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The trust policy for the role specifies who can assume it:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "cognito-idp.amazonaws.com"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "UNIQUE_EXTERNAL_ID"
        }
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

This trust policy allows the role to be assumed by Cognito user pools (indicated by the Principal element). However, this is restricted to cases where the external ID in the AssumeRole API request matches the value of the sts:ExternalId key in the trust policy.

The external ID is a globally unique identifier (GUID) generated by Cognito when the role is created. It ensures that only the specific user pool associated with this role can assume it. I plan to explore external IDs and the confused deputy problem in more detail in a separate post.

To view the external ID for a user pool, we can call the DescribeUserPool API:

{
  "UserPool": {
    "SmsConfiguration": {
      "SnsCallerArn": "arn:aws:iam::ACCOUNT_ID:role/service-role/ROLE_NAME",
      "ExternalId": "UNIQUE_EXTERNAL_ID",
      "SnsRegion": "eu-central-1"
    },
    // Other properties in the response have been omitted
  }
}
Enter fullscreen mode Exit fullscreen mode

As a result, other user pools can't assume this role, as their external IDs will differ.

3.2. From SMS Sandbox to production

Amazon SNS places new SMS accounts into a sandbox for security reasons. While the account is in the sandbox, text messages can only be delivered to a maximum of 10 verified phone numbers.

The sandbox is useful for development and testing purposes but is unsuitable for a production environment where hundreds or thousands of users need to receive text messages. For this reason, you may want to transition the account from the sandbox to the production environment. More information about this process can be found in the links section at the bottom of the page.

3.3. Enable the passwordless option

Under the Authentication section, we can enable one or more passwordless options. Available choices include Passkey, Email message one-time password, and SMS message one-time password. Multiple options can be selected, but for now, let's proceed with the SMS message.

3.4. Modify the App Client

Next, we must enable the USER_AUTH flow in the app client configuation if it has not already been enabled.

Enabling USER_AUTH flow

This can be done by selecting the checkbox next to the ALLOW_USER_AUTH setting under the Authentication flows section in the App client menu. This configuration allows users to sign in using methods other than a username and password, such as a one-time passcode (OTP), which aligns with our passwordless authentication goal.

3.5. Create a user without a password

We can now create a user with a phone number, or they can sign up using their phone number if this option is enabled in the user pool.

User without password

If you are using a sandbox environment, ensure that a verified phone number is added to the user. To make the verification code received via SMS the sole authentication method, set the Temporary password to Don't set a password.

3.6. It works!

When we call the authorization server endpoint, which ends in /oauth2/authorize, the managed UI will display only the username field.

Username input box

Enter the username of the newly created user configured to sign in exclusively with a passcode. Next, the UI will prompt for the verification code sent to the user's specified phone number.

Verification code input box

If, however, we attempt to log in a user with a password as their only sign-in option, clicking Next in the Sign in pop-up will automatically display the Password field. Cognito user pools dynamically determine which input field to show based on the username provided.

4. Considerations

It's possible to add the text message option and other passwordless options to a newly created user pool, so using an existing user pool is not mandatory.

What if we want a single user to have both password and SMS-code options? Will it work?

Yes. If a user is created with both password and phone number sign-in enabled, the managed UI will display the Try another way option.

Try another way

Here, users can select from the available options, such as SMS one-time password and Password. The user can then decide how they want to authenticate.

Choose from different sign-in methods

5. Summary

Cognito user pools offer passwordless sign-in options for application users. One of these options is a verification code sent via text message.

The user pool utilizes SNS to send text messages to phone numbers. Configuring this involves several steps, including setting up permissions in both SNS and Cognito, moving the account out of the sandbox for production, and enabling the SMS passwordless option in the user pool. Once configured, the managed UI will automatically display the appropriate sign-in page based on the user's settings.

6. References, further reading

Getting started with user pools - How to create a user pool with an app client

IAM role creation - How to create IAM roles

Sending SMS messages using Amazon SNS - The title says it all

Moving out of the Amazon SNS SMS sandbox - The process to send text messages to any phone numbers from SNS

Top comments (0)