AWS STS token refresh with existing token received from AssumeRoleWithSAML - amazon-web-services

I have a use-case where I need to have temporary AWS STS token made available for each authenticated user (auth using company IDP). These tokens will be used to push some data in AWS S3. I am able to get this flow, by using SAML assertion in IDP response and integrating with AWS as SP (IDP initiated sign-on) similar to one shown here.
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_saml.html#CreatingSAML-configuring
But as STS allows token validity to be max for 1 hour, I want to refresh those tokens before expiry so that I don't have to prompt user to give credentials again (bad user experience). Also as these are company login credentials, I cant store them in the application.
I was looking at AWS IAM trust policy, and one way to do this is adding 'AssumeRole' entry to the existing SAML trust policy as shown below (second entry in the policy)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::xxxxxxxxxxxx:saml-provider/myidp.com"
},
"Action": "sts:AssumeRoleWithSAML",
"Condition": {
"StringEquals": {
"SAML:aud": "https://signin.aws.amazon.com/saml"
}
}
},
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:sts::xxxxxxxxxxxx:assumed-role/testapp/testuser"
},
"Action": "sts:AssumeRole"
}
]
}
So for first time when testuser logs in as uses AssumeRoleWithSAML API/CLI, he will get temporary credentials. Next, he can use 'AssumeRole' API/CLI with those credentials, so that he can keep on refreshing the tokens without requires IDP credentials.
As can be seen, this works only for STS user with ARN of "arn:aws:sts::xxxxxxxxxxxx:assumed-role/testapp/testuser" for refreshing tokens as he/she can assume that role. but I need a generic way, where for any logged in user, he can generate STS tokens.
One way is to use wildcard characters in Trust policy for Principal, but looks like it is not supported. So I am stuck with tacking credentials every time the tokens expire. Is there a way to solve this?
thanks,
Rohan.

I have been able to get this working by specifying a role instead of an assumed-role in the IAM trust policy. Now my users can indefinitely refresh their tokens if they have assumed the testapp role.
"Principal": {
"AWS": "arn:aws:sts::xxxxxxxxxxxx:role/testapp"
},

AWS STS supports longer role sessions (up to 12 hours) for the AssumeRole* APIs. This was launched on 3/28/18, here is the AWS whats-new link: https://aws.amazon.com/about-aws/whats-new/2018/03/longer-role-sessions/. By that you need not to do a refresh as I assume a typical workday is < 12 hours :-)

Your question is one I was working on solving myself, we have a WPF Desktop Application that is attempting to log into AWS through Okta, then use the AssumeRoleWithSaml API to get the STS Token.
Using this flow invoked the Role Chaining rules and thus our token would expire every hour.
What I did to overcome this is to cache the initial SAMLResponse Data from Okta (after the user does MFA) and use that information to ask for a new Token every 55 minutes. I then use that new token for any future AWS resource calls.
Once 12 hours passes, I ask the user to authenticate with Okta again.
For those wondering about implementation for their own WPF apps, we use the AWS Account Federation App in Okta.
The application uses 2 packages:
Okta .NET Authentication SDK
AWS SDK for .NET
After setting up your AWS Account Federation App in Okta, use the AWS Embed Url and SAML Redirect Url in your application to get your SAMLResponse data.

Related

Authorization using Api gateway and Lambda

Hi I am trying to implement Custom authorization using api gateway and lambda. My current understanding is as follows. I have created simple GET method and deployed to Dev Enviroment. Create lambda authorize to return the IAM policy. I used python blue print api-gateway-authorizer-python. Below is the format of response we should get.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Deny",
"Resource": "arn:aws:execute-api:us-east-1:{ACCOUNTID}:{APIID}/ESTestInvoke-stage/GET/"
}
]
}
In the above IAM policy, Resource is ARN of my Api Dev stage. What is Action indicates? Also to test this now, How can I get token? I want to test it from postman? I am just confused here. I have my AWS account and authorization is nothing but my current account has access to this Dev stage? How internally it works? To store all the permissions do we need to maintain any other DB? Can someone help me to understand this? Any help would be appreciated. Thanks
To get a token you need an identity provider. Amazon Cognito is one of those (Google, Facebook works as well). To understand that policy you have to understand the chain of commands.
Suppose a client calls an API endpoint (GET /orders), this will trigger a service Lambda so the token can be verified. If the verification is successful, another Lambda (GetOrder a business Lambda this time) will be invoked by Api Gateway.
If your service Lambda (Lambda authorizer) will return a policy like this:
{
"principalId": "apigateway.amazonaws.com",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": "arn:aws:execute-api:{REGION}:{ACCOUNT_ID}:{API_ID}/Prod/GET/"
}]
}
}
the API Gateway service (i.e. the principalId equals to apigateway.amazonaws.com) is allowed (i.e. Effect equals to Allow) to invoke (i.e. Action equals to execute-api:Invoke) the given API resource (e.g. Resource equals to arn:aws:execute-api:{REGION}:{ACCOUNT_ID}:{API_ID}/Prod/GET/).
In your case the ARN that you return is related to the tester of API Gateway, but it should point to your real function.
This article may help.
Ok, what are custom authorisers for API gateway: custom authorisers let you define your own authentication & authorisation logic.
How do you get the token: Thats part of your authentication and authorisation logic, If you are deploying your services on AWS, you can use AWS cognito. API gateway also supports cognito authorization.
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html
Using postman to test? it's handy to use postman. I use it.
How does the permission internally work: You can use a token to authenticate a user. (If you are using a JWT token, you can also verify the user's claims).
Do you need an internal db? this is entirely depends on your use case. if your use case is simple as all users treated equal, you might not need a db. lets say some users can access some additional features, you may still not need a db (you can use claims). but if your application becomes complicated and you have to manage different access permissions, users, groups, etc, you may surely need a db.

IAM Policy Cognito Variables for DynamoDB LeadingKeys Restriction

My setup:
- Mobile Hub
- Cognito User Pool
- Api Gateway
- DynamoDB
What I got working so far:
The User can sign up/in with the Cognito User Pool and get an Id and AccessToken.
The IdToken is used with the Api Gateway Cognito Authorizer to access the Api Gateway.
The Mapping of the user sub into the integration message to DynamoDb works.
"userId": {
"S": "$context.authorizer.claims.sub"
}
Restricting the access to non user rows in a DynamoDb Table does not work.
The DynamoDb tables were created using Mobile Hubs Protected Table feature, which creates the following policy:
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": [
"${cognito-identity.amazonaws.com:sub}"
]
}
}
But thats not working, because this expression returns the Identity User Id and NOT the User Pool Sub. At first I'm not using Identity Pools and second I want to use the User Sub here.
I found out
${cognito-idp.<REGION>.amazonaws.com/<POOL-ID>:sub} should do the trick, but thats not working too.
If I hardcode the Condition to use the Sub of my test user, everything works as expected, so the Policy itself is okay, it's only the expression to get the sub of the current user is not working correctly.
Is it possible to debug the IAM Policys to see what the values of the expressions are at runtime?
Any Ideas, hints, suggestions?
Thanks in advance.
I got the answer now from an AWS Dev. You are only able to use the
${cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXXX:sub}
variable if you configure the Cognito User Pool as an open ID Connect provider directly against IAM.
But there is a big problem, because you need to update the SSL Thumbprint of the service endpoint if the certificate changes in the Open ID Connector configuration. But you are not able to tell when the aws certificate has changed.
I finally figured this out by using aws:PrincipalTags
Pre-req is making sure that the IAM role that the Cognito User assumes has sts:TagSession assume role policy permission. This allows principal tags to be passed after successful login to Cognito.
On the Cognito Identity Pool, open Authentication Providers and find the Cognito provider. Make sure that Attributes for access control uses the default mappings which maps username to sub, or you have a custom rule set that passes the sub property to a PrincipalTag.
Finally you can use the tag passed in the filter of the role to only allow access to resources by that sub.
{
"Action": [
"dynamodb:UpdateItem",
"dynamodb:Query",
"dynamodb:PutItem",
"dynamodb:GetItem"
],
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": "${aws:PrincipalTag/username}"
}
},
"Effect": "Allow",
"Resource": "arn:aws:dynamodb:*:*:table/MyTableName",
"Sid": ""
}

Access Cognito User ID variable in IAM role definied for API Gateway execution role?

With the recent release of API Gateway Cognito Custom Authorizers, I'm attempting to use Cognito, API Gateway and S3 together for authenticated access control without Lambdas.
Authorizing with API Gateway works as it should (with Trust Relationships for the API Gateway execution role set correctly) but I can't seem to get the resource policy to capture the Cognito User ID Sub variable for fine grain access control to S3 resources based on User ID.
Here's the current flow I'm trying to accomplish:
Authenticate with Cognito and get valid token
Send token to API Gateway to gain access to S3 bucket (through AWS Service integration type)
Fine grain access to only User ID's directory
Return S3 object (based on API endpoint)
Here's my current resource policy for the API Gateway execution role:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cognito-idp:*",
"cognito-sync:*",
"cognito-identity:*"
],
"Resource": [
"*"
]
},
{
"Effect": "Allow",
"Action": [
"s3:Get*",
],
"Resource": [
"arn:aws:s3:::mybucket/${cognito-identity.amazonaws.com:sub}/*"
]
}
]
}
Everything works as it should but this IAM variable (in the policy attached to the API Gateway execution role) doesn't seem to be right.
I came across this StackOverflow article and tried using both formats us-east-1:xxxx-xxxx-xxxx-xxxx and xxxx-xxxx-xxxx-xxxx but both didn't seem to work. I'm using the sub attribute found in the Cognito User Pool User info. If I hard code the folder in S3 to the Cognito User ID Sub it works just fine.
How do I get the Cognito variable to work in the API Gateway's execution role policy?
Here are a couple other articles I found related to the question on the AWS forums:
Cognito IAM variables not working for assumed-role policies
What cognito information can we use as IAM Variables?
That's not the sub that the variable expects. There is no way to use cognito user pool attributes in policy. The sub that you want is the cognito identity id which is the id of the user in the cognito Identity (federated identity pool). You can get this ID by using the get id method. I would suggest you store this ID as a custom attribute variable in your cognito user pool so you don't have to keep making the call.
You can read more about this identity id here.

AWS IAM STS: proper way to make temporary admin?

I want to be able to assign a time-based api token to a non-admin AWS user that results in giving that user temporary admin privileges to all AWS services.
Why do I want this? Because when I develop on AWS on my personal account I like to be able to have admin access to every service, but I don't want to have a pair of cleartext undying admin credentials sitting in my .aws/credentials file. So I want to be able to assume an IAM role that will elevate a user to admin and use STS to assign a time-based API token.
At work we use federation via a SAML server so users are given time-based access no matter what role they have: dev, admin, etc, but I don't want to have to set all of that up just to have a time-based API token. I have read the AWS docs and discussed this in #aws and so far the response I have is to make an IAM trust policy that hard-codes a time end:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*",
"Condition" : {"DateLessThan": {"aws:CurrentTime" : "2017-10-30T00:00:00Z"}}
}
]
}
But I don't want to manually hardcode and update this policy every time and would rather use STS to assign a time-based API token. Any insight would be much appreciated.
Have you tried GetSessionToken , refer this
Sample Request:
https://sts.amazonaws.com/
?Version=2011-06-15
&Action=GetSessionToken
&DurationSeconds=3600
&SerialNumber=YourMFADeviceSerialNumber
&TokenCode=123456
&AUTHPARAMS
STS and IAM Roles:
1) Create your role in the AWS console.
2) Use the AWS CLI to issue you new credentials using this role. You can create a batch script with the command to simplify executing it.
Example:
aws sts assume-role --role-arn arn:aws:iam::123456789012:role/xaccounts3access --role-session-name s3-access-example
The output of the command contains an access key, secret key, and session token that you can use to authenticate to AWS.
Temporary credentials

How to reduce a cognito userpool user to their own folder in S3 bucket?

I am writing a basic react native app where users will be able to register themselves to an AWS cognito userpool and log in with that identity to store/retrieve their data from S3. I only have one bucket and every user will have their own folder in that bucket. How can I restrict each user to their own folder in that case. Here is the scenario.
I created two users in the user pool.
I then created a federated identity for my userpool. This federated identity has two IAM roles, authorized and unauthorized.
I then added a policy to the auth role of federated identity.
Here is my policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "arn:aws:s3:::mybucket/${cognito-identity.amazonaws.com:sub}/*"
}
]
}
I then tried to retrieve data from S3 using Javascript SDK and I could ListObjects from "album-b207a8df-58e8-49cf-ba1b-0b48b7252291" where "b207a8df-58e8-49cf-ba1b-0b48b7252291" is the sub of "madi" user. Why was "test2" able to list that object?
Can you provide a snippet of the onClick_Cognito_receiptsdumpAuth_Role.*** ??
My guess (without your logs)
is that your policy is probably good, but you might have a policy
that grants list access to too much.
Your AWS class is being inited with your developer credentials
(which might have full Admin)
Cognito might have an issue and its
worth logging a support ticket.
Next steps I would try is
You might have a action:List* or equivalent
Also best hidden secret (it's not really a secret) is the policy simulator.
Test your policy against that and it will tell you if at least the policy is good and don't forget that iam policies are concatenated.
Lastly, if you can't figure out how the access is provided to the List Operation, you can enable CloudTrail to dump API logs to S3 and verify that the listobjects is being run by the cognito user you are expecting.