How do I use remembered devices in my Amazon Cognito user pool?

7 minute read
0

I want to track devices that users in my Amazon Cognito user pool use to sign in to my app.

Short description

Amazon Cognito can track and remember devices that users in a user pool use to sign in. With device remembering, you can set sign-in restrictions, such as limit signing in from a single device. Or, you can let users skip repeated sign-ins on the same device. For more information, see Specifying user pool device tracking settings.

Note: You must use the USER_SRP_AUTH authentication flow to use the device tracking feature. To use remembered devices to replace multi-factor authentication (MFA), you must also turn on MFA for your user pool.

Remembering devices is a two-part process:

  • Confirm a new device: Initiate authentication from the device, and then confirm it with Amazon Cognito to get unique device identifiers.
  • Verify a confirmed device: Initiate authentication from a confirmed device to skip the MFA step on subsequent sign-in attempts.

Resolution

Note: These instructions describe the Amazon Cognito API calls to make in your app client's code. If you use a client-side SDK, such as the AWS Mobile SDKs, then the SDK handles much of the implementation.

Set up remembered devices

  1. In the Amazon Cognito console, choose Manage user pools, and then choose your user pool.
  2. In the navigation pane, under General settings, choose Devices.
  3. For Do you want to remember your user's devices, choose Always or User Opt In. For more information, see Tracking and remembering devices using Amazon Cognito Your User Pools.
  4. For Do you want to use a remembered device to suppress the second factor during multi-factor authentication (MFA), choose Yes or No. For more information, see Using remembered devices to suppress multi factor authentication (MFA).
    Note: If these options don't appear, then MFA isn't activated. To set up MFA, in the navigation pane, choose MFA and verifications.
  5. Choose Save changes.

Call SetUserMFAPreference

In your app client's code, call the SetUserMFAPreference API to set the MFA preference to true for the MFA methods that you use.

Call InitiateAuth

In your app client's code, call the InitiateAuth API to get the device keys. In this API request, include these request parameters:

  • AuthFlow: Use USER_SRP_AUTH.
  • AuthParameters: Include the authentication parameters USERNAME, SRP_A, and SECRET_HASH.
    Note: SECRET_HASH is required only if your app client is configured with a client secret.

Use this formula to calculate SRP_A:
SRP_A = ga (mod N)

  • Use g as defined in AuthenticationHelper.js. For the definition of g, see AuthenticationHelper.js on the GitHub website.
  • Let a = 128 random bytes

For more information, see Request syntax and the API reference for your chosen AWS SDK.

Call RespondToAuthChallenge for MFA challenges

After your app client makes the InitiateAuth API call, your Amazon Cognito user pool returns a set of MFA challenges. The MFA challenges are based on the MFA methods that you activate.

Your app must use the RespondToAuthChallenge API to answer these challenges. For example, if you activated SMS text message MFA in your user pool, then include the parameter ChallengeName with the value SMS_MFA. For more information, see Request syntax and the API reference for your chosen AWS SDK.

Store the device keys

When all MFA challenges are answered, Amazon Cognito responds with a DeviceGroupKey and a unique DeviceKey in the NewDeviceMetadataType field.

When you use AWS Mobile SDK for Android, iOS, or JavaScript in the Browser, the application automatically moves these keys to the device's local storage. If you use another AWS SDK, then be sure to design a similar key storage solution for your app.

Call ConfirmDevice

With the DeviceGroupKey and DeviceKey, use the Secure Remote Password (SRP) protocol to create a secret. This generates a salt and a password verifier. Pass these to Amazon Cognito in a ConfirmDevice API call that includes the following request parameters:

  • AccessToken: Use a valid access token for the user.
  • DeviceKey: Use the unique key for the device, returned from Amazon Cognito.
  • DeviceName: Use a name that you give to the device. (The AWS Mobile SDKs use User Agent.)
  • DeviceSecretVerifierConfig: Include Salt (16 random bytes, Base64-encoded) and PasswordVerifier.

For PasswordVerifier, use the following formula:
PasswordVerifier = g( SHA256_HASH(salt + FULL_PASSWORD) ) (mod N)

  • Use g as defined in AuthenticationHelper.js. For the definition of g, see AuthenticationHelper.js on the GitHub website.
  • Let FULL_PASSWORD = SHA256_HASH(DeviceGroupKey + username + ":" + RANDOM_PASSWORD)
  • Let RANDOM_PASSWORD = 40 random bytes, base64-encoded
  • Use N as defined in AuthenticationHelper.js. For the definition of N, see AuthenticationHelper.js on the GitHub website.

For more information, see request syntax and the API reference for your chosen AWS SDK.

(Optional) Call UpdateDeviceStatus

When you set up remembered devices in your user pool, you can skip this step if you choose Always. If you choose User Opt In, then you must include an UpdateDeviceStatus API call.

Note: If you use the Amazon Cognito Identity SDK for JavaScript, then you must instead call setDeviceStatusRemembered or setDeviceStatusNotRemembered. For information on how to us Amazon Cognito Identity SDK for JavaScript , see Usage on the GitHub website.

When you give your app's users an option to have their device remembered, device confirmation tracks in your user pool as 'non-remembered.' You must ask the user if they want to have the device remembered, and then use the UpdateDeviceStatus API to send their input.

Note: When opt-in is required, the user must opt-in before the device can be remembered and the MFA challenge can be suppressed.

Call InitiateAuth with the device key

In your app client's code, call the InitiateAuth API to verify a remembered device. In this call, include the device key in the request parameters as follows:

  • AuthFlow: Use USER_SRP_AUTH.
  • AuthParameters: Include the authentication parameters USERNAME, SRP_A, and DEVICE_KEY.

For more information, see request syntax and the API reference for your chosen AWS SDK.

Call RespondToAuthChallenge for DEVICE_SRP_AUTH

After your app client makes the InitiateAuth API call with a valid device key, your Amazon Cognito user pool returns the PASSWORD_VERIFIER challenge. You must include the DEVICE_KEY in the challenge response. Then, you receive the DEVICE_SRP_AUTH challenge. To respond, call the RespondToAuthChallenge API and include the following request parameters:

  • ChallengeName: Use DEVICE_SRP_AUTH.
  • ClientId: Use a valid app client ID.
  • ChallengeResponses: Include USERNAME, DEVICE_KEY, and SRP_A in these responses.
    Note: For SRP_A, use the formula mentioned earlier in these instructions.

After this API call, Amazon Cognito responds with one more challenge: DEVICE_PASSWORD_VERIFIER. In the response, you also get the values for the ChallengeParameters SECRET_BLOCK and SRP_B. You need the SECRET_BLOCK and SRP_B values to respond to the challenge.

For more information, see request syntax and the API reference for your chosen AWS SDK.

Call RespondToAuthChallenge for DEVICE_PASSWORD_VERIFIER

To respond to the DEVICE_PASSWORD_VERIFIER challenge, call the RespondToAuthChallenge API and include the following request parameters:

  • ChallengeName: Use DEVICE_PASSWORD_VERIFIER.
  • ClientId: Use a valid app client ID.
  • ChallengeResponses: Include USERNAME, PASSWORD_CLAIM_SECRET_BLOCK, TIMESTAMP, PASSWORD_CLAIM_SIGNATURE, and DEVICE_KEY.

Define the challenge responses as follows:

  • For PASSWORD_CLAIM_SECRET_BLOCK, use the value of SECRET_BLOCK.
  • For TIMESTAMP, include the current time. (For example, Tue Sep 25 00:09:40 UTC 2018.)
  • Let PASSWORD_CLAIM_SIGNATURE = SHA256_HMAC(K_USER, DeviceGroupKey + DeviceKey + PASSWORD_CLAIM_SECRET_BLOCK + TIMESTAMP), base64-encoded
  • Let K_USER = SHA256_HASH(S_USER)
  • Let S_USER = [ ( SRP_B - [ k * [ (gx) (mod N) ] ] )(a + ux) ](mod N)
  • Let x = SHA256_HASH(salt + FULL_PASSWORD)
  • Let u = SHA256_HASH(SRP_A + SRP_B)
  • Let k = SHA256_HASH(N + g)

Get JSON web tokens from your user pool

After you successfully respond to the last challenge, your Amazon Cognito user pool returns JSON web tokens in the AuthenticationResult:

{
    "AuthenticationResult": {
        "AccessToken": "...",
        "ExpiresIn": 3600,
        "IdToken": "...",
        "RefreshToken": "...",
        "TokenType": "Bearer"
    },
    "ChallengeParameters": {}
}

Related information

User pool authentication flow

User pools reference (AWS Management Console)

CognitoUser.js on the GitHub website

AWS OFFICIAL
AWS OFFICIALUpdated 7 months ago
2 Comments

I've tried implementing this in multiple languages, but can't get it to work anywhere. It seems to me that in the ConfirmDevice step of the flow, this part is incorrect:

Let FULL_PASSWORD = SHA256_HASH(DeviceGroupKey + username + ":" + RANDOM_PASSWORD)

Since later on the signature is calculated as

SHA256_HMAC(K_USER, DeviceGroupKey + DeviceKey + PASSWORD_CLAIM_SECRET_BLOCK + TIMESTAMP)

it seems to make more sense that username in the FULL_PASSWORD should instead be DeviceKey...

replied 10 months ago

Thank you for your comment. We'll review and update the Knowledge Center article as needed.

profile pictureAWS
MODERATOR
replied 10 months ago