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": ""
}
Related
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.
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.
In a traditional MySql Server situation, as the owner of a database, I create a User and from the database I grant certain access rights to the User object. An application can then (and only) access the database by supplying the password for the User.
I am confused and don't see a parallel when it comes to giving access to a DynamoDB table. From the DynamoDB Tables page, I can't find a means to grant permission for an IAM user to access a table. There is an Access Control tab, but that appears to be for Facebook/Google users.
I read about attaching policies but am confused further. How is access controlled if anyone can create a policy that can access all tables?
What am I missing? I just want to create a "login" for a Node application to access my DynamoDB table.
If anyone in your AWS account can create IAM policies you have a real security issue.
Only a few accounts should do that (Create IAM policies).
DynamoDB accesses work along with IAM user like you said, so, you need to do the following:
Create IAM groups to classify your IAM users, for example, DBAGroup for dbas, DEVGroup for developers and so on.
Create IAM policies to grant specific access to your DynamoDB tables for each group.
Apply the policies to the specific groups for granting accesses.
For login purposes, you need to develop a module that will verify the credentials with IAM service, so you need to execute IAM API calls. This module could be deployed within an EC2, could be a Javascript call to an API Gateway's endpoint along with a Lambda function, Etc.
What you need to do:
Create an account on IAM service that will be able to execute API calls to the IAM service for verifying credentials (Login and password).
This account should have only permissions for doing that (Verify user login and password).
Use the API credentials to be able to execute API calls.
If you don't want to create your own module for login purposes, take a look at Amazon Cognito
Amazon Cognito lets you add user sign-up/sign-in and access control to your web and mobile apps quickly and easily. Cognito scales to millions of users and supports sign-in with social identity providers such as Facebook, Google, and Amazon, and enterprise identity providers via SAML 2.0.
The last step is how your module execute API calls to IAM service? As you may know, we need API Credentials. So, using the logged user's credentials you will be able to execute API calls to read data from tables, execute CRUD operations, Etc.
To set specific permissions for certain tables as in SQL Server you must do this:
In Identity and Access Management (IAM) create a new security policy on the JSON tab:
Use the following JSON as an example to allow a full CRUD or remove the items within the "Action" section to allow only the desired items:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListAndDescribe",
"Effect": "Allow",
"Action": [
"dynamodb:List*",
"dynamodb:DescribeReservedCapacity*",
"dynamodb:DescribeLimits",
"dynamodb:DescribeTimeToLive"
],
"Resource": "*"
},
{
"Sid": "SpecificTable",
"Effect": "Allow",
"Action": [
"dynamodb:BatchGet*",
"dynamodb:DescribeStream",
"dynamodb:DescribeTable",
"dynamodb:Get*",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:BatchWrite*",
"dynamodb:CreateTable",
"dynamodb:Delete*",
"dynamodb:Update*",
"dynamodb:PutItem"
],
"Resource": "arn:aws:dynamodb:*:*:table/MyTable"
}
]
}
Give the policy a name and save it.
After that, go to the Identity and Access Management (IAM) Users screen and create a new user as shown below.
Remember to set the field ** Access type ** as * Programmatic access *, it is not necessary to add the user to a group, click on "Atach existing policies directly" and add the policy previously created.
Finished! You already have everything you need to connect your application to Dynamodb.
I've already gone through the documentation and it closely mirrors my use-case except that I cannot use Google, Facebook or Amazon as my identity provider, but I already have an enterprise level OAuth 2.0 access token for authenticated users.
I understand that I could possibly use Enterprise Federated support from AWS STS to get temporary credentials and use them to further access the AWS resources but I fail to understand how can i configure the IAM Policy to use these credentials to achieve horizontal information hiding.
I have certain tables in DynamoDB in which I store the details of all the users of my application and my application supports multiple tenants so I want the users of one tenant to NOT being able to access data of other tenants. The IAM policy that I could configure is of the type:
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": ["${www.amazon.com:user_id}"]
}
}
Now my users are NOT logged in via Amazon ( or Google or Facebook ) and hence I cannot use the keys like "${www.amazon.com:user_id}" etc. Also my hash key for some tables are composite.
So my question is how to achieve multi-tenancy at the database level and be able to segregate or separate data per tenant i.e. to hide certain rows of my tables from the users who should not have access on it.
Is it possible to specify custom Policy Variables while defining the IAM policy and specify how to resolve those at runtime ? Or some other way be ?
My tables in Dynamo currently have composite hash keys, which are a combination of Tenant_ID and User_ID so can I specify some kind of rule in the IAM Policy so that I should be able to achieve horizontal information hiding ?
Please let me know if you need more information about my use case.
Regards,
Agraj
In order to enable fine-grained data access in DynamoDB, you must specify an IAM Policy Element Variable in the DynamoDB IAM policy.
A typical policy may look like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "FullAccessToUserItems",
"Effect": "Allow",
"Action": [
"dynamodb:*"
],
"Resource": [
"arn:aws:dynamodb:*:table/*"
],
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": [
"${cognito-identity.amazonaws.com:sub}"
]
}
}
}
]
}
Where ${cognito-identity.amazonaws.com:sub} is an IAM policy variable representing the user's sub in Cognito.
Unfortunately Amazon do not publish a list of available policy variables. What this does mean though is that your user management has to be managed through Amazon to enable fine grained security. You cannot define your own policy variables - they have to be pre-defined Amazon variables - hence fine grained security is only available where your users are managed in Amazon.
Additionally your DynamoDB partition key has to match the policy variable. For example you table partition key would have to be the Cognito sub.
If your OAuth token was received from Cognito you can simply post it to the Amazon Token Endpoint, which will give you back an id_token which contain the users Cognito sub.
I am using the AWS Javascript API and trying to get the assigned cognito id:
AWS.config.credentials.get(function(err) {
if (!err) {
console.log("Cognito Identity Id: " + AWS.config.credentials.identityId);
}
});
Why does this result in a 400 error with the message below?
{"__type":"InvalidIdentityPoolConfigurationException","message":"Invalid identity pool configuration. Check assigned IAM roles for this pool."}
I have IAM roles configured for authenticated and non-authenticated users.
{
"Version": "2012-10-17",
"Statement": [{
"Action": [
"mobileanalytics:PutEvents",
"cognito-sync:*"
],
"Effect": "Allow",
"Resource": [
"*"
]
}]
}
The most common reason for this error is your roles aren't set up to trust your identity pool. You should confirm that the identity pool id listed in your trust relationships matches the identity pool you are using.
More info on trust relationships in Amazon Cognito can be found in our developer guide.
After some digging I realized that you must add the RoleArn and AccountId to your credentials.
Even though most of the documentation out there mention this as being enough:
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxx-a87e-46ed-9519-xxxxxxx',
});
This was not enough.
I had to do this:
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxx-a87e-46ed-9519-xxxxx',
RoleArn: 'arn:aws:iam::xxxxx:role/Cognito_xxxxUsersUnauth_Role',
AccountId: 'xxxxxxxxx', // your AWS account ID
});
You must mention the ARN of your Role for your identity pool.
The only doc that mention it right is this one.
The wrong ones:
http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/browser-configuring.html
https://mobile.awsblog.com/post/TxBVEDL5Z8JKAC/Use-Amazon-Cognito-in-your-website-for-simple-AWS-authentication
https://blogs.aws.amazon.com/javascript/post/TxTUNTVES4AL15/Authentication-in-the-Browser-with-Amazon-Cognito-and-Public-Identity-Providers
Maybe I'm missing something but this is certainly confusing.
Check the "Trust Relationship" section of the role that is assigned to your Identity Pool, authentication users.
Make sure you have policies defining access to your Cognito pool.
The easiest way to get the requirement policy statements is,
Edit the pool
Create new role for identity pool
In IAM edit this role to copy the policy statements
Add these Trust Relationships to your required existing role
Another - probably less common - reason: Make sure that you are actually using an identity pool and if not, remove the identity pool id from your aws-exports.js.
I was getting this error after adding federated sign ins to my user pool (not identity pool). For reasons unknown my config included an aws_cognito_identity_pool_id. Removing this id solved the error for me.
I checked the Trust Relationship of my roles configured for "Authenticated role" and "Unauthenticated role" for my identity pool more than once, but still the error occured.
After reviewing my whole identity pool configuration I recognized that in
Authentication providers
Cognito
Authenticated role selection
I have chosen "Choose role from token" and my wrong configured role was the one I attached to the cognito group for my users.
So updating the Trust Relationship for this role fixed the problem.
Hope this helps someone :)
In my case, I am using SAML identity provider. The action in the IAM role policy should be: "Action": "sts:AssumeRoleWithSAML". But this is the root cause of the exception. I have to manually change it to "Action": "sts:AssumeRoleWithWebIdentity". It turns out any role created by the Cognito identity pool will use "Action": "sts:AssumeRoleWithWebIdentity". It won't check your identity provider type. I believe this is a bug.
I encountered this error and my problem turned out to be that my user was assuming an unauthenticated role because I was returning AWSTask(result:nil) from the logins() function in my custom CognitoDeveloperIdentityProvider.
I had the same error when trying to retrieve files from S3 through my Identity pool users.
Solution: You can create a role in IAM for "Web Identity". Then provide your identity pool ID and add the permissions that you want the role to have, e.g. S3FullAccess. Then navigate back to Amazon Cognito Identity pools and assign the role you just created to the unauthrole or authrole. The users in the Identity pool should now be able to access the S3 resources
Another -way less probably scenario- is that either the provider or identityPoolId you are using is invalid. I spent hours debugging a missing ENV in my code.
Had this issue, after several hours of checking our what the problem could be, found out that the Trust Policy is actually missing this line sts:TagSession in the Action List so eventually the Authenticated Trust Policy is as defined below:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": [
"sts:AssumeRoleWithWebIdentity",
**"sts:TagSession"** //this does the trick for me
],
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "{IDENTITY_POOL_ID}"
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "authenticated"
}
}
}
]
}