AWS Developer Tools Blog

Authentication with Amazon Cognito in the Browser

Amazon Cognito is a great new service that enables a much easier workflow for authenticating with your AWS resources in the browser. Although web identity federation still works directly with identity providers, using the new AWS.CognitoIdentityCredentials gives you the ability to provide access to customers through any identity provider using the same simple workflow and fewer roles. In addition, you can also now provide access to unauthenticated users without having to embed read-only credentials into your application. Let’s look at how easy it is to get set up with Cognito!

Setting Up Identity Providers

Cognito still relies on third-party identity providers to authenticate the identity of the user logging into your site. Currently, Cognito supports Login with Amazon, Facebook, and Google, though you can also setup developer authenticated identities or OpenID Connect. You can visit the respective links to setup applications with these providers which you will then use to get tokens for your application as normal. After you have created the applications with the respective providers, you will want to create an identity pool with Amazon Cognito.

Creating an Identity Pool

Cognito’s “identity pool” is the core resource that groups the users in your application. Effectively, each user logging into your application will get a Cognito ID; these IDs are all created and managed from your identity pool. The identity pool needs to know which identity providers it can hand out IDs to, so this is something we configure when creating the pool.

The easiest way to create an identity pool is through the Amazon Cognito console, which offers a good walkthrough of the necessary parameters (the name, your identity provider application IDs, and whether or not you want to support unauthenticated login). In our example we will use the SDK to create an identity pool with support for Amazon logins and unauthenticated access only. We will call the pool “MyApplicationPool”:

// Cognito Identity is currently only available in us-east-1
var ci = new AWS.CognitoIdentity({region: 'us-east-1'});

var params = {
  AllowUnauthenticatedIdentities: true,
  IdentityPoolName: 'MyApplicationPool',
  Logins: {
    'www.amazon.com': 'amzn1.application.1234567890abcdef'
  }
};
ci.createIdentityPool(params, console.log);

This will print an IdentityPoolId, which we can now use to get a Cognito ID for our user when logging in.

Authenticated vs. Unauthenticated Roles

Now that we’ve configured our pool, we can start talking about logging the user in. There are two ways for a user to login to your application when you enable unauthenticated access:

  1. Authenticated mode — this happens when a user goes through the regular Amazon, Facebook, or Google login flow. This type of login is considered authenticated because the user has proven their identity to the respective provider.
  2. Unauthenticated mode — this happens when a user attempts to get a Cognito ID for your pool but without having authenticated with an identity provider. If you enabled AllowUnauthenticatedIdentities, this will be allowed in your application. This is useful when your application has features you want to expose to users without requiring login, like, say, being able to view comments in a blog.

The way we separate these two “modes” in AWS is through IAM roles. We are going to create two roles for our application matching these two types of users. First, the authenticated role:

Authenticated Role

The easiest way to create a role is through the IAM Console (click “Create Role”). Let’s create a role called “MyApplication-CognitoAuthenticated”, choose a “Role for Identity Provider Access”, granting access to web identity providers. You should now be in “Step 3: Establish Trust”. This is the most important part; it is where we limit this role only to authenticated Cognito users. Here’s how we do it:

  1. Select Amazon Cognito and enter the Identity Pool Id
  2. Be sure to click Add Conditions to add an extra condition.
  3. Set the following fields:
    • Condition: “StringEquals”
    • Key: “cognito-identity.amazonaws.com:amr”
    • Condition Prefix: “For any value”
    • Value: “authenticated”

This will permit only authenticated users to use this role. The following example is what your trust policy should look like after clicking Next:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Federated": "cognito-identity.amazonaws.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "cognito-identity.amazonaws.com:aud": "us-east-1:1234-abcd-dcba-1234-4321"
        },
        "ForAnyValue:StringLike": {
          "cognito-identity.amazonaws.com:amr": "authenticated"
        }
      }
    }
  ]
}

Next, set the correct role permissions for this authenticated user. This step will depend heavily on your application and the actual resources you want to expose to authenticated users. You can use the policy generator here to help you create a policy that is scoped to specific actions and resources.

This should give you the correct authenticated role for use in your application.

Unauthenticated Role

This role should be configured almost like the authenticated role except for a few differences:

  1. We give it a separate name like “MyApplication-CognitoUnauthenticated”
  2. We set the Trust Policy condition value to “unauthenticated” instead of authenticated.
  3. The permissions on the role should be much more restrictive, and typically only exposing read-only operations (like for reading comments in a blog).

Once you’ve created these roles you should be able to copy down both role ARNs. These two ARNs will be used when logging into the application under the respective “modes”.

Logging In

We’ve now created all the resources we need to login to our application, so let’s see the code used in the AWS SDK for JavaScript to enable this login flow.

Initially, you will likely want all users to login under “unauthenticated” mode. This can be done up front when configuring your application:

AWS.config.update({
  region: 'us-east-1',
  credentials: new AWS.CognitoIdentityCredentials({
    AccountId: '1234567890', // your AWS account ID
    RoleArn: 'arn:aws:iam::1234567890:role/MyApplication-CognitoUnauthenticated',
    IdentityPoolId: 'us-east-1:1234-abcd-dcba-1234-4321'
  })
});

Eventually your customer will authenticate with the associated Amazon application. In that case, you will want to update your credentials to reflect the new role and provide the web token. A portion of the Login with Amazon code might look like:

amazon.Login.authorize({scope: "profile"}, function(resp) {
  if (!resp.error) { // logged in
    var creds = AWS.config.credentials;
    creds.params.RoleArn =
      'arn:aws:iam::1234567890:role/MyApplication-CognitoAuthenticated';
    creds.params.Logins = {
      'www.amazon.com': resp.access_token
    };

    // manually expire credentials so next request will fire a refresh()
    creds.expired = true;
  }
});

Getting the Identity ID

Occasionally, you will need the user’s Cognito identity ID to provide to requests. Since the ID is a unique ID for the user, it’s likely you will use this token for things like the user’s identifier in your application. In general, you can get the ID from the identityId property on the credentials object, but it’s likely you will want to use a recently refreshed credentials object prior to accessing the property. To verify that the ID is up to date, wrap this access inside of the get() call on a credentials object, for example:

AWS.config.credentials.get(function(err) {
  if (!err) {
    var id = AWS.config.credentials.identityId;
  }
});

Wrapping Up

That’s all there is to using Amazon Cognito for authenticated and unauthenticated access inside of your application. Using Cognito gives your application plenty of other benefits, such as the ability to merge a user’s identity if they choose to login with multiple identity providers (a common feature request), as well as the ability to take advantage of the Amazon Cognito Sync Manager to easily sync any user data inside of your application.

Take a look at Cognito login in the AWS SDK for JavaScript and let us know what you think!