AWS Single Sign-On (SSO) Using STS
I offer an immense pleasure for you showing up here. Being a computer science devotee proficient in Microservices Development, Cloud Computing & Data Engineering, I have always looked forward in learning, sharing & gaining knowledge with overwhelming support of brilliant minds like you. With my deep regards, I hope this article will be able to reach out to the desire you wish to read ahead. Thankyou!
— Kshitiz
If you are looking out to build a functionality which is capable to login in AWS console by generating a temporary session for your application’s users, then this article is where your search ends. We will be understanding the entire flow in a very summarized manner with an end-to-end example.
Problem Statement
You are building an application which internally integrates with AWS services. You are asked to add a redirect button on app screen which upon clicking should open a temporary automatically signed-in AWS console window of the S3 bucket specific for users. The AWS window should display the IAM role and the Email-ID of that application user.
Solution
The requirement is straight enough to understand that we need to generate a temporary session for users to enable them AWS console access. In order to do that, we would be taking help from AWS STS AssumeRole API provided to us from it’s SDKs. AssumeRole helps us to generate credentials based on an IAM role we request for, and in return we can generate a temporary session out of those credentials for logging in to the console. Here is how it goes step by step:
- Create an AWS IAM Policy for app users
- Create an AWS IAM Role and attach the policy created in previous step
- In application codebase, create a STS client (Eg: Using boto3 in Python)
- Generate credentials using AssumeRole API
- Generate sign-in token using above credentials
- Return final redirect URL ready to be logged in
Additionally, Please note that you have these access permissions before proceeding:
- To create an AWS IAM Policy
- To create an AWS IAM Role
- AWS Access Key ID
- AWS Secret Access Key
Create an AWS IAM Policy for app users
So the very first step is to create an IAM policy which will have permission to only certain resources. In this example, I have created two S3 buckets:
- developers-of-my-app
- users-of-my-app
We will allow only users-of-my-app bucket to be accessed from AssumeRole. Now we will create an IAM policy specifying this bucket as the only resource permitted. Only List & Read permission are provided in this example.
Create an AWS IAM Role for app users
While creating AWS IAM role, we need to make sure to update Trust Relationships of the role to enable it to get assumed from an AWS resource. AWS allows only admins to assume a role unless STS AssumeRole permission is provided to resources explicitly.
In my case I am allowing another role to assume this role and in order to do that, STS AssumeRole permission has been added to it’s policy. You can put any user’s ARN also in place of role’s ARN. After this once the policy created in previous step is attached, the role should be created successfully and ready to be assumed from it’s trustworthy resources.
Create a STS Client
Now we will go ahead into our application codebase and create a STS client using AWS SDK. I am using Python and boto3 framework to the same for this example:
sts_client = boto3.client('sts',
aws_access_key_id = AWS_ACCESS_KEY_ID,
aws_secret_access_key = AWS_SECRET_ACCESS_KEY,
region_name='us-east-1'
)
Generate Credentials Using AssumeRole API
The STS client create previously will call AssumeRole API by accepting two paramters, role name and session name.
credentials = sts_client.assume_role(
RoleArn = "arn:aws:iam::XXXXXXXXXXXX:role/role-of-my-app",
RoleSessionName = "abc@xyz.com"
)
Here RoleArn is the ARN of the role we created earlier. Please remember to update XXXXXXXXXXXX with your 12-digit AWS account ID. RoleSessionName is the parameter we can pass to display the email-id of application user after login in AWS console.
Generate Sign-In Token Using Above Credentials
To login in AWS console specifically, we need a sign-in token. This token can be generated from the credentials returned by AssumeRole call.
url_credentials = quote(json.dumps({
"sessionId": credentials["Credentials"]["AccessKeyId"],
"sessionKey": credentials["Credentials"]["SecretAccessKey"],
"sessionToken": credentials["Credentials"]["SessionToken"]
}))signin_params = "?Action=getSigninToken"
signin_params += "&SessionDuration=1800"
signin_params += f"&Session={url_credentials}"url = "https://signin.aws.amazon.com/federation" + signin_paramssignin_token = json.loads(requests.get(url).text)["SigninToken"]
Here the SessionDuration parameter specifies the validity of the session which is 30 minutes (1800 seconds) in this example. It should be atleast 15 minutes and atmost 12 hours as per AWS policies.
Generate Final Redirect URL
Since we have received sign-in token, we can now login to AWS console to our desired URL using the above session. For this example, we are using S3 URL to redirect automatically for that user.
https://s3.console.aws.amazon.com/s3/buckets/users-of-my-app
url = "https://s3.console.aws.amazon.com/s3/buckets/users-of-my-app"redirect_url = "https://signin.aws.amazon.com/federation"
redirect_url += "?Action=login"
redirect_url += "&Issuer=my-app.com"
redirect_url += "&Destination=" + quote(url)
redirect_url += f"&SigninToken={signin_token}"print(redirect_url)
The resultant output when pasted in browser will not only login in AWS console for 30 minutes but also redirect to the destination path you have provided.
Also, to test for second bucket, we will get access denied error because this bucket is not permitted in our IAM policy.
Complete Code
import json
import boto3
import requests
from urllib.parse import quotests_client = boto3.client('sts',
aws_access_key_id = AWS_ACCESS_KEY_ID,
aws_secret_access_key = AWS_SECRET_ACCESS_KEY,
region_name='us-east-1'
)credentials = sts_client.assume_role(
RoleArn = "arn:aws:iam::XXXXXXXXXXXX:role/role-of-my-app",
RoleSessionName = "abc@xyz.com"
)url_credentials = quote(json.dumps({
"sessionId": credentials["Credentials"]["AccessKeyId"],
"sessionKey": credentials["Credentials"]["SecretAccessKey"],
"sessionToken": credentials["Credentials"]["SessionToken"]
}))signin_params = "?Action=getSigninToken"
signin_params += "&SessionDuration=1800"
signin_params += f"&Session={url_credentials}"url = "https://signin.aws.amazon.com/federation" + signin_paramssignin_token = json.loads(requests.get(url).text)["SigninToken"]url = "https://s3.console.aws.amazon.com/s3/buckets/users-of-my-app"redirect_url = "https://signin.aws.amazon.com/federation"
redirect_url += "?Action=login"
redirect_url += "&Issuer=my-app.com"
redirect_url += "&Destination=" + quote(url)
redirect_url += f"&SigninToken={signin_token}"print(redirect_url)
References
AssumeRole AWS Security Token Service
Thanks again for spending those precious couple of minutes of yours reading the article. For any query or suggestion please feel free to reach out: