Hello There!!

When designing an application one of the pivotal area of interest is how to authenticate and authorize the users who are logging into your application. Consider, you choosing AWS as your cloud provider, you will be utilizing its EC2 service for deploying your application, S3 service for storing your files and SQS service for queuing messages. But how do you determine whether a logged in user only has access to read the S3 files and not to send messages to SQS?

In this blog, I will be sharing a granular way to access AWS resources with Role Based Access Control in place which will systemize the authentication and authorization of an application. Amazon Cognito, one of the services in AWS allows us to accomplish this use-caseThere are a lot of blogs on the Internet which talks about authentication and are more concentrated on Android integrations. This blog is specially targeted for Java developers with examples for both authentication and authorization and the same can be integrated with web services.

AWS Cognito has two pool types,

  1. User Pools – Used to add authentication and user management to mobile and web applications.
  2. Identity Pools – Allows users to temporarily access AWS resources via STS.

Problem Statement:

There are two users Alice and Bob in a company.

  • Alice works with the finance department where she will be adding payroll transactions to the database
  • Bob, on the other hand, works as a relationship officer who is responsible for sending messages to the external clients.

Both have different roles and their access to company resources differ. 

Solution:

For the given problem statement,

  • Create roles in IAM
  • Create Users and Groups in Cognito User Pool
  • Create Identity and attach authentication provider in Cognito Identity Pool

Deployment Setup:

Infrastructure Readiness:

Create User Pool
  1. Create a User Pool, company_user_pool with an Application Client
  2. Copy both User Pool ID and App Client ID for further use
Create Identity Pool
  1. Create a new Identity pool, company_identity_pool for accessing company resources.
  2. This identity pool should not provide access to unauthenticated users. Under “Unauthenticated Identities”, keep Enable access to unauthenticated identities option unchecked
  3. Once done setup authentication providers. There are multiple authentication providers available for Identity pool.
  4. Choos Cognito as authentication provider and pass company_X’s Pool ID and App Client ID
  5. After Identity Pool is created, click on “Edit Identity Pool”
  6. Under “Authentication Provides” there is “Authentication role selection”. From the drop-down, select “Choose role from token” and select “Role resolution” to DENY
  7. Now copy the Identity pool ID from the top and save changes.
Create Roles
  1. Create two roles in IAM
  2. Select “trusted entity” as Web Identity and “Identity Provider” as Amazon Cognito
  3. Now paste the Identity Pool ID copied earlier. select required policies, enter the role name from below and create the role.
  4. payroll Role –  will have read/write access Dynamo DB for adding payroll transactions
  5. messaging Role –  will have access to SNS and SQS which send messages to external clients
Create Users and Groups in User Pool
  1. Inside the user pool, Create two users one for Alice and another for Bob
  2. Now create two groups, finance and external_communications. Attach payroll role to finance group and messaging role to external_communications group.
  3. Once groups are created attach User Alice to finance group and Bob to external_communications group
IAM User – For Programmatic Access

The setup done till now is used to access AWS services in a granular way. But for this to work via Java code, a superuser should have access to Cognito User and Identity pools. There are multiple ways of doing this,

  1. Create an IAM role with access to Amazon Cognito and attach the role to the EC2 instance in which your application is running. (This will work if you have an application deployed in EC2)
  2. Else create an IAM Role with read/write access to Amazon Cognito, attach it to an IAM user and then generate secret and access key.

Step 2 is feasible if development is done on the local machine.

Note: The IAM user and role created is only for programmatically accessing the AWS Cognito service and is not a part of User Management the application holds. Application users are created in Cognito User pool and access to AWS services like SNS and DynamoDB are provided through Cognito Identity pool.

Design:

Implementation

For the given use case below are the AWS SDK dependency to be added to the Java application. Find the Maven/Gradle way of adding the dependency in this link.

  • aws-java-sdk-cognitoidp  (User Pool)
  • aws-java-sdk-cognitoidentity  (Identity Pool)
  • aws-java-sdk-sns  (Simple Notification Service)
  • aws-java-sdk-sqs  (Simple Queuing Service)
  • aws-java-sdk-dynamodb (Dynamo DB)
Authentication

Below is the code sample in java to authenticate the users.

Once the login is successful Cognito responds with AuthenticationResult which has an ID, Access and Refresh Token. Each token has its purpose and we will be using the ID token for Authorizing the request. This ID token when decoded has the necessary information for Cognito Identity pool to authorize. Below is the decoded value of ID token generated for users Alice and Bob.

 

 

 

 

Authorization
  1. Create a AmazonCognitoIdentity client with AWS Credentials created from the IAM user for programmatic access.

  2. Build a logins map which will have the Cognito User Pool ID as the key and the ID token generated after successful login with Cognito User Pool.

  3. Construct GetIdRequest with AWS Account ID, logins map and Cognito Identity Pool ID

  4. Fire a getId request through AmazonCognitoIdentity client.

  5. Again with AmazonCognitoIdentity client fire getCredentialsForIdentity with getIdResult and logins map.

  6. A successful response will give GetCredentialsForIdentityResult from where credentials can be fetched.

  7. Now construct BasicSessionCredientials.

  8. This BasicSessionCredientials can now be used to build AWS clients. Below snippet creates an Amazon SNS client.

  9. Once AmazonSNS client is build, messages will be published to the selected topic.

Dynamo DB client can also be created the same way as step 8.

Test Cases
  • Login in with Alice – Add a payroll item to Dynamo DB
  • Login in with Bob – Publish a message to an SNS topic
  • Login in with Alice/Bob – Try accessing other services which are not a part of the role.

Execution will fail if the user does not have the necessary privileges to access a service.

Conclusion:

AWS Cognito allows us to have a fine-grained control over the AWS resources. Cognito User pool with Users, Groups, and IAM Roles in place, provides the administrators an ease of use in assigning users the necessary privileges and authenticating the same. On the other hand, Cognito Identity pool restricts the logged in users on what to do and what not to do by monitoring the identities and providing temporary access for the AWS resources.

References:

Amazon Cognito – https://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html

Amazon RBAC – https://docs.aws.amazon.com/cognito/latest/developerguide/role-based-access-control.html

Fine Grained Authorisation – https://aws.amazon.com/blogs/mobile/building-fine-grained-authorization-using-amazon-cognito-user-pools-groups/