Map ForgeRock OpenAM Roles to AWS Cognito Role-Based Access Control (RBAC) to control access to AWS services (S3, API Gateway, DynamoDB etc.)
In the previous blog, we did a SAML Federation setup between AWS Cognito and OpenAM using OpenID Connect and SAML. In this blog, we are going to extend this setup to map OpenAM roles to Cognito Role-Based Access Control.
Cognito provides rich set of features for RBAC like assigning different roles to Authenticated / Anonymous users, rule based mapping to assign roles to users. Roles are basically AWS IAM roles which defines permissions to access various AWS services like S3, API Gateway, DynamoDB, EC2 etc.
Many organizations have in house IAM systems which typically uses a LDAP based user store like OpenDJ or Microsoft AD etc. With LDAP datastore, users can be assigned to different groups. If the users want to access various AWS services based on the group mapping, there should be some way to map these LDAP groups to AWS IAM roles. This is where Cognito’s RBAC comes into picture.
Below is an architecture of how OpenAM roles (LDAP groups) can be mapped to AWS IAM roles:
- For this POC, we are going to use Spring Boot based web application. User initiates the login in web app.
- Spring Boot app initiates an authorization code grant OIDC flow with Cognito.
- Cognito in turn automatically makes a SAML federation request to OpenAM and receives an assertion.
- If the SAML assertion is valid, Cognito will create / update the user profile in local user pool and return an authorization code to Spring Boot app.
- Spring Boot app exchanges the authorization code for id_token, access_token and refresh_token
- If the id_token is valid, Spring Boot app shows the home page.
- User can perform various actions on AWS services. In this case, it is S3. But, in reality, it can be any service.
- Spring boot app uses enhanced flow to exchange the id_token for temporary credentials from AWS STS using Cognito Identity Pool.
- Cognito Identity pool automatically verifies the id_token with Cognito User pool.
- If the id_token is valid, Identity Pool will invoke AWS STS to get temporary credentials for this user based on the IAM role.
- AWS STS will verify the IAM role and return the credentials to Identity Pool.
- Identity Pool returns the temporary credentials which is a combination of access key+secret key+session token.
- Spring Boot app uses these temporary credentials to access various AWS services.
There are few prerequisites to setup this application
- AWS account — free tier or corporate
- Knowledge on AWS services like Cognito, IAM
- Knowledge on Java, Spring framework, Federation protocols (OAuth, OIDC, SAML)
Step 1: Setup SAML Federation between AWS Cognito and local OpenAM instance
Follow this blog to configure Cognito — OpenAM SAML Federation.
Step 2: Configure OpenAM to send role attribute in SAML assertion
- In Step 1, we already configured the SAML federation between OpenAM and AWS Cognito. In this step, we are going to map one more attribute called “ismemberof” from OpenAM to Cognito.
- Login to OpenAM console Navigate to “Realms > AWS > Data Stores” tab and select “embedded” datastore link.
- Scroll down to “LDAP User Attributes” list and add “ismemberof” attribute. Save the changes
- “ismemberof” is the LDAP attribute which stores the user’s group membership
- Navigate to “Federation” tab and select “http://openam.example.com:8080/idpam” IDP in AWS realm
- Select “Assertion Processing” tab and add “role=ismemberof” in “Attribute Map” list
- Navigate back to “Realms > AWS > Subjects > Group” and create two new groups “AWS_S3_ADMIN” and “AWS_S3_READ”
- Select “User” tab and create 2 users s3read and s3admin.
- Select the “Group” tab for s3read user and assign “AWS_S3_READ” group.
- Similarly, select “Group” tab for s3admin user and assign “AWS_S3_ADMIN” group.
- This should automatically update the “ismemberof” attribute of both these users.
- Now, access the below URL as mentioned in previous blog. Login with s3read once, then open a new browser session and login with s3admin. Also, keep the SAML tracer plug-in open in browser to check the SAML assertion.
- With this change, you should see a new SAML attribute in Assertion response
For s3admin user:
</saml:Attribute>For s3read user:
Step 3 : Map OpenAM role to Cognito custom user attribute
- Now that OpenAM is configured to send the role attribute in SAML assertion, next step is to map this role to a custom Cognito attribute.
- Navigate to AWS Management console and select “openamuserpool” in Cognito service. Then select “General Settings > Attributes”
- Select “Add custom attribute” link and name the attribute as role. Once changes are saved, attribute name will change to “custom:role”
- Select “General Settings > App Clients”, then select “Set attribute read and write permissions” for openamagent.
- Select “custom:role” checkbox for both Readable and Writable attributes.
- Select “Federation > Attribute Mapping > SAML” and add a new mapping for “role = custom:role” to map role SAML assertion attribute to Cognito’s custom role attribute.
- Once these changes are done, initiate the authentication flow again with the same URL and authenticate with “s3read” and “s3admin” credentials.
- Once the authentication is successful, navigate to Cognito openamuserpool “General Settings > Users and Groups”. You should see 2 user entries.
- Click each user entry and you should see the new attribute “custom:role” populated.
- s3admin user will have a role “AWS_S3_ADMIN”
- s3read user will have a role “AWS_S3_READ”
Step 4: Configure Cognito Identity Pool and setup Role-based Access Control based on OpenAM roles
- Now that Cognito user pool is setup and integrated with OpenAM, next step is to setup the Cognito Identity Pool.
- Navigate to Cognito > Manage Federated Identities screen and “Create a new identity pool” and name it as openamidpool
- In “Authentication Providers > Cognito”, enter the openamuserpool “Pool Id” value
- Enter the “App client id” as openamagent which was created in user pool.
- Click “Create pool” and in the next screen, just click “Cancel”
- Select “Dashboard > Edit identity pool” and copy the “Identity pool ID”
- Open a new tab and navigate to “IAM > Roles” and click “Create role”
- Select “Web identity” option and “Amazon Cognito” in “Identity provider”. Enter the “Identity pool ID” copied in previous step. Click Next
- Search for s3 and select “AmazonS3ReadOnlyAccess” policy.
- In the next screen, enter role name as “S3_READ_ONLY” and create role.
- Follow the same steps to create one more role called “S3_ADMIN”
- The only difference will be policy name should be “AmazonS3FullAccess” and role name should be S3_ADMIN.
- After creating both the roles, open these role definitions and check the values.
- If you select the “Trust relationships” tab, you will see “Trusted entities” as “ cognito-identity.amazonaws.com” with a condition “stringEquals” and “ cognito-identity.amazonaws.com:aud” assigned to Identity Pool ID. This makes sure that this particular role can be used by this Cognito Identity Pool.
- Go back to the “openidpool” in Cognito service and “Edit Identity pool”. Scroll down to “Authentication providers > Cognito” tab.
- In “Authenticated role selection”, select “Choose role with rules”.
- Enter the claim : custom:role, value: contains, Role : AWS_S3_ADMIN, S3_ADMIN
- Add another rule with claim : custom:role, value: contains, Role : AWS_S3_READ, S3_READ_ONLY
- Save the changes
Step 5: Setup a local Spring Boot app
- Now that we have a Cognito User pool integrated with OpenAM, Cognito Identity Pool integrated with the User pool, let us setup a Spring Boot web application which will talk to User Pool and authenticate a user using OpenAM. Using the OpenID Connect idtoken received from Cognito User Pool, this app will talk to Cognito Identity Pool and get temporary credentials to access AWS services. For the purpose of POC, we are going see read bucket, create bucket. But in a real scenario, it can be integrated with any AWS service like API Gateway, Lambda, RDS etc.
- Download the Spring Boot app from https://github.com/awskarthik82/spring-aws-openam
- Unzip the https://github.com/awskarthik82/spring-aws-openam/blob/master/spring-openam-aws/mvn.zip within the same folder. You should see a .mvn folder at the same level as src folder.
- Modify the following values in https://github.com/awskarthik82/spring-aws-openam/blob/master/spring-openam-aws/src/main/resources/application.yml
cognitoIdentityId: <identity pool ID>cognitoProvider: cognito-idp.us-east-1.amazonaws.com/<User pool ID>cognitoRegion: us-east-1 //If you modify cognitoRegion, modify the region in cognitoProvider property as wellclient-id: <user pool app client openamagent id>client-secret: <app client openam agent secret>authorization-uri: <user pool domain URL>/oauth2/authorize token-uri: <user pool domain URL>/oauth2/token
jwk-set-uri: https://cognito-idp.us-east-1.amazonaws.com/<user pool ID>/.well-known/jwks.json
- If it is windows, run mvnw.cmd spring-boot:run to start the application
- If it is unix, run ./mvnw spring-boot:run to start the application
- Make sure the local openam instance in http://openam.example.com:8080/idpam is up and running.
- Access http://localhost:9080 which is the Spring Boot app URL. Since there is no session, it will just show a link for Cognito.
- Click that link and it should automatically redirect to OpenAM login page.
- Login with s3read user credentials and that should redirect to this page.
- You can check the “Display User Info” which will display the logged in user details like firstname, lastname, custom:role etc.
- If you select “List S3 Buckets”, it will show all the buckets along with objects in your account. In the back end, Spring Boot app makes a call to Cognito’s Identity Pool to get temporary AWS credentials to access the S3 APIs.
- Go back to home page and select “Create S3 Buckets”
- You can try creating a bucket and that should throw an Access Denied error because s3user belongs to AWS_S3_READ group in OpenAM which is mapped to “S3_READ_ONLY” AWS IAM role in Cognito Identity Pool.
- Open a new incognito window and again access http://localhost:9080. Now login with s3admin user which is mapped to “AWS_S3_ADMIN” openam group.
- Try creating a new bucket and it should work. You can again select “List S3 buckets” to check the newly created bucket. This worked because “AWS_S3_ADMIN” OpenAM role is mapped to “S3_ADMIN” AWS IAM role in Cognito Identity Pool.
- Now, navigate back to Cognito Identity Pool “openamidpool” and check the “Identity Browser”. You should 2 entries for the 2 user.
- These entries got created when Spring Boot app made a credentials API call to AWS to get temporary credentials using OpenID Connect idtoken.
We have successfully setup Cognito — OpenAM Integration and also mapped OpenAM roles to Cognito Role-based Access Control. In this way, organizations can control their Internal / External IAM users access to various AWS services.
Thanks for reading this blog. If you have any questions / suggestions, kindly leave a comment.