In an android app, I receive a JWT access_token from http://<domain>.auth.<region>.amazoncognito.com/login once the user is done authenticating to a Cognito User Pool. That User Pool is linked to a Cognito Identity Pool.
What API should I call with that access_token to get an AWSCredentials object.
The closest one I found would be AssumeRoleWithWebIdentity, but that is an STS API, and some of what I've read on the web seems to recommend developers not use STS directly but rely on Cognito.
Moreover, I do not expect the API I need to require specifying a role name. Cognito Identity Pools are already configured to give authenticated users a specific role. And AssumeRoleWithWebIdentity takes a role name as input to the API. Hence that does not look like right.
I've looked at Cognito Identity Pool API Reference, and can't find an API that takes access_token and return AWS credentials.
UPDATE:
The following answer which uses GetCredentialsForIdentity throws ResourceNotFoundException saying it cannot find the specified IdentityId.
string access_token = ...
var jwtAccessToken = System.IdentityModel.Tokens.Jwt.JwtSecurityToken(access_token);
var client = new AmazonCognitoIdentityClient(new AnonymousAWSCredentials(),REGION);
var response = await client.GetCredentialsForIdentityAsync(new GetCredentialsForIdentityRequest
{
IdentityId=String.Format("{0}:{1}", REGION, jwtAccessToken.id),
Logins=new Dictionary<string,string>
{
{String.Format("cognito-idp.{0}.amazonaws.com/{1}", REGION, USER_POOL_ID),
access_token}
}
});
After much investigation, I found the answer.
1- One needs an id_token not an access_token to authenticate to Cognito, as misleading as this might sound. AWS's documentation which says you ask for id_token when you need to have user attributes like name / email etc... and ask for an access_token when you don't need that information and just want to authenticate is wrong, or at the very least misleading.
2- And here's how you use an id-token to get AWS Credentials:
var credentials = CognitoAWSCredentials(<identity pool Id>, region);
credentials.AddLogin(
"cognito-idp.<region>.amazonaws.com/<user_pool_id>",
id_token); // the raw token
Note that you do not need AssumeRoleWithIdentity, or GetCredentialsWithIdentity, you do not even need a AmazonCognitoIdentityClient.
To get the credentials you can use GetCredentialsForIdentity method by passing the JWT token. This method is implemented in AmazonCognitoIdentityClient class in the AWS Android SDK.
IAM Role should be defined in the Cognito Federated Identities. This limits the assuming role to be handled internally, by Cognito not allowing the mobile app to assume any other role than the one configured. In addition you shouldn't give this role IAM permission, allowing the Android SDK to assume different roles (Unless its a superuser kind of a user who is logging in).
Related
I have a simple application consisting of two modules - OAuth2 client (Spring Cloud Gateway) and OAuth2 resource. I use AWS Cognito as an identity provider. Some users with specific roles should be able to create/update/delete other Cognito users.
I'm going to use aws-java-sdk-cognitoidp library for integration with AWS Cognito API, but I need to make this calls as a current logged-in user, to be able to check user's groups and IAM roles. Because users from different groups should have different privileges.
I found AWSSessionCredentialsProvider that seems suitable for this task, but there is no implementation.
I implemented this provider to get access token from the SecurityContext. When I call the API I get an error:
The security token included in the request is invalid
Is it possible to use user's access token as a credentials for AWSCognitoIdentityProvider?
I used to have app to login with given user with his credentials -
AWSCognitoIdentityProvider cognitoIdentityProvider = AWSCognitoIdentityProviderClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(getSDKCredentials()))
.withRegion(Regions.fromName(getAwsRegion()))
.build();
Map<String,String> params =new HashMap<>();
params.put("USERNAME", userName);
params.put("PASSWORD", passCode);
AdminInitiateAuthRequest initialRequest=new AdminInitiateAuthRequest()
.withAuthFlow(AuthFlowType.ADMIN_NO_SRP_AUTH)
.withAuthParameters(params)
.withClientId(getClientId())
.withUserPoolId(getUserPoolId());
AdminInitiateAuthResult response = cognitoIdentityProvider.adminInitiateAuth(initialRequest);
this will return your response where you have session. check if it helps-
In my js file, I am accessing my AWS sns, but the main issue is that currently, I am hard coding my IAM access and security keys... Is there a way to avoid doing this by using a temporary key or hiding my keys? I have posted a snippet on how I am updating the config below.
var AWS = require('aws-sdk');
AWS.config.update({
region: < 'My Hard coded region' >,
credentials: {
accessKeyId: < 'My Hard coded Access Key' >,
secretAccessKey: <'My Hard Coded Security Key'>
}
});
...
PLEASE HELP.
It's very hard to comment without knowing more about your code.
The react native is a mobile app. You will need a service to authenticate your mobile app users against the service. One such service is amazon Cognito.
Userpool
Amazon Cognito has a concept called user pool. The user pool is where the users are stored.
When you authenticate a user against a user pool, you will get a authorised token in return. Your app can store this token and send it to your backend services. The backend services can validate the token to confirm the identity of the user.
Identity pool
The identity pool is a concept where temporary AWS credentials are issued in exchange for an authorised token. This is the temporary AWS credentials, using which you can call aws services directly from the mobile app.
Hope this helps.
here is an article for you to read - https://pusher.com/tutorials/serverless-react-native-aws-amplify
Yes there are much more secure ways to handle credentials. Hard coding them is the least secure.
While it is possible to do so, we do not recommend hard-coding your AWS credentials in your application. Hard-coding credentials poses a risk of exposing your access key ID and secret access key
See https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html
Also see From Best Practices for Managing AWS Access Keys:
Don't embed access keys directly into code. The AWS SDKs and the AWS Command Line Tools allow you to put access keys in known locations so that you do not have to keep them in code.
See https://docs.aws.amazon.com/general/latest/gr/aws-access-keys-best-practices.html
The most secure method is to load the credentials from AWS Identity and Access Management (IAM) roles for Amazon EC2.
Each SDK has a way to do this. Check your SDK documentation for loading credentials from IAM.
Here is a good overview for credentials best practices, with examples in Java: https://aws.amazon.com/blogs/developer/credentials-best-practices/
I came across this stackoverflow while googling how to solve this problem. Here's the approach I ended up using to solve this problem in case this helps anyone else. It will expand on the identity pool answer provided earlier.
My setup was this:
Static website hosted on s3, so no way to make AJAX calls back to the server itself to bypass hardcoding credentials.
The solution I ended up using was Cognito identity pools as mentioned. The setup involved creating an identity provider in IAM. We were using an company internal OpenID identity provider. Then the next step was to create an identity pool in cognito that pointed to that identity provider. Next was to provision an IAM role for signed in users of that identity pool to give them access to whatever resources you are trying to secure.
On the s3 website, we had already integrated with the internal OpenID identity provider, so it was just a matter of extracting the token and passing it to Cognito.
The program flow is as follows:
User logs in to open ID internal system.
This system returns a token
Code extracts token and then creates a Cognito Identity Credentials
https://docs.aws.amazon.com/cognito/latest/developerguide/getting-credentials.html
The below is a code snippet copy pasted from the docs linked above. Note that the Logins object is where we will pass the token. The key will be the "provider" field value configured in IAM.
AWS.config.region = 'us-east-1';
// Configure the credentials provider to use your identity pool
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'IDENTITY_POOL_ID',
Logins: { // optional tokens, used for authenticated login
'graph.facebook.com': 'FBTOKEN',
'www.amazon.com': 'AMAZONTOKEN',
'accounts.google.com': 'GOOGLETOKEN',
'appleid.apple.com': 'APPLETOKEN'
}
});
// Make the call to obtain credentials
AWS.config.credentials.get(function(){
// Credentials will be available when this function is called.
var accessKeyId = AWS.config.credentials.accessKeyId;
var secretAccessKey = AWS.config.credentials.secretAccessKey;
var sessionToken = AWS.config.credentials.sessionToken;
});
The call to cognito will return temporary credentials including a session token, which you can now pass to your other aws clients.
I am an AWS newb, so please go easy on me :)
I have setup a proof of concept to proove out an authenticated API backed by lambda with the following components.
API Gateway -> backed by Lambda
Federated Identities backed by AWS Cognito UserPool
I have the authorizer setup in the API gateway to use the IAM role which is being provided by the Federated Identity pool.
I can see the identity (ap-southeast-2:<GUID>) coming through into the gateway ( using this in my integration request mapping template "$context.identity.cognitoIdentityId" ) from https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html#context-variable-reference
From either the gateway or lambda how can I resolve the 'ap-southeast-2:<GUID>' back to the source identity which resides in the user pool. (E.g. Pull one of the custom attributes from it)
Other information if relevant, i'm using Amplify-AWS for the client calling into the API Gateway.
TIA.
As you're noticing, Cognito as an identity provider is not the same as Cognito as a user pool.
Federated Identities provide a way of giving someone identified access to your AWS resources. The identity_id the identity provider gives you can almost be thought of as a tracking code. CIP (Congito [Federated] Identity Provider) allows you to get an identity id by signing in through any number of providers (not just the user pool), and even by not signing in at all.
User Pools give you a way of managing users for your application (i.e. a set of usernames, emails, passwords, etc).
This is the reason getting from identity_id back to the user pool user is hard (because, there's no guarantee it is a user pool user, it could well be someone from Facebook).
From what you've said, however, the assumption that said identity_id came from a UserPool authentication is safe. This means you have two options:
The official way will be to use identity:GetOpenIdToken to convert identity_id (you can ignore the logins part of the request) into an OpenId token. You can then use this token against the userpools:GetUser end point. There's a few pitfalls here, like ensuring you authenticate with a scope that allows you to see all the attributes you care about.
Curiously, however, the value of cognitoAuthenticationProvider is not opaque, and can (unoffically) be decoded:
// Cognito authentication provider looks like:
// cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxxxxxxx,cognito-idp.us-east-1.amazonaws.com/us-east-1_aaaaaaaaa:CognitoSignIn:qqqqqqqq-1111-2222-3333-rrrrrrrrrrrr
// Where us-east-1_aaaaaaaaa is the User Pool id
// And qqqqqqqq-1111-2222-3333-rrrrrrrrrrrr is the User Pool User Id
The above example, with more details about how you can then use this with userpools:AdminGetUser can be found here: https://serverless-stack.com/chapters/mapping-cognito-identity-id-and-user-pool-id.html
I was reviewing a website's javascript files to find an endpoint i wanted,during this i found a line like this.
DASHBOARD_API_Token=eyJraWQiOiJVS2paTnB3SWdBK292QzB3RWdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
When i decoded the Above JWT i found entities like kid, username, useremail, iss = http://cognito-idp.us-east-1.amazonaws.com/{pool-id}
I have read few amazon resources which tell that its possible to fetch aws credentials using the above token. Given that i only have the jwt token what is possible and is it that i have to contact the website to revoke the current token?
Well, AWS Cognito is quite an interesting beast when it comes to its JWT tokens and what you can do with them.
Firstly, when you authenticate the user against Cognito User Pool, you get 3 different tokens: AccessToken, IdToken, and RefreshToken. Both AccessToken and IdToken are valid for exactly 1 hour (and you can't change it). This is why you get RefreshToken, which you can use to get new pair of AccessToken and IdToken by calling, for example, InitiateAuth. RefreshToken is by default valid for 30 days, but you can change it in the settings of your Cognito User Pool.
AccessToken
This is the most powerful token that is given by Cognito. If you possess a valid AccessToken, you can call several quite sensitive Cognito APIs.
One very surprising example is that you can call DeleteUser API by providing AccessToken and it will completely remove your account from Cognito User Pool. The "nicest" thing here is that app developer can't stop you from doing it if you've got a valid AccessToken. AWS devs are saying that "its by design". That was one of the strongest reasons why we decided not to pass Cognito Access Token onto the application frontend.
Speaking of the payload of AccessToken, the only potentially sensitive claim is the client_id, which contains the Cognito Client Id, which was used to authenticate the user. If this client does not have a client secret (which should probably be considered a security flaw in the first place), then the client_id can be used to call quite wide range of Cognito APIs. However, Cognito considers client_id to be public information. All other claims are considered public, you can see the full list of claims here.
IdToken
Contains the information about the player. It may contain some personal information about the player depending on what standard claims and custom attributes were configured in your Cognito User Pool, however it is also possible to suppress or override those claims using Pre Token Generation Lambda Trigger. Using this trigger allows one to hide or mask player attributes, which are considered sensitive and should not appear in IdToken.
As far as I'm aware, you cannot user IdToken to call Cognito APIs.
RefreshToken
Can only be used to request new AccessToken and IdToken, cannot be used to call Cognito APIs. In our particular use case we had to avoid passing RefreshToken to the app frontend, since it can be used to get AccessToken against public Cognito API (meaning that we cannot stop the user from doing that).
Using Cognito tokens to get AWS credentials
This is only possible if Cognito User Pool has been explicitly integrated with Cognito Identity Pool (which assumes that app developers knew what they were doing). When you exchange your Cognito JWT tokens, you get temporary AWS credentials with permissions bounded to a specific IAM role configured in Cognito Identity Pool. It is completely legitimate flow and intended behaviour. Moreover, if Cognito Identity Pool is configured to allow unauthenticated access, you can even get temporary AWS credentials without having Cognito JWT tokens (but those will be tied to another, presumably, more restrictive IAM role).
Using Cognito tokens with API Gateway
AWS API Gateway resources can be integrated with Cognito User Pools. Depending on how the integration is done, you can use either an AccessToken or IdToken from the integrated Cognito User Pool.
Revoking the tokens
You can revoke all tokens issued for your account in Cognito User Pool by calling GlobalSignOut API and providing a valid AccessToken. As this API is public, you don't need to contact the website if you possess a valid AccessToken.
If my user send a request to STS requesting for credentials:
AWS.config.credentials = new AWS.WebIdentityCredentials({
RoleArn: 'arn:aws:iam::{id}:role/{role}',
WebIdentityToken: idToken,
RoleSessionName: VALUE
});
Later, when the user sends a request to a private API Gateway endpoint, by using RoleSessionName I could know who is the person making the request. But how can we avoid another user to assume his identity by using the same RoleSessionName?
What is the best way to authenticate users using STS and IAM roles?
RoleSessionName is just an identifier for the assumed session. It shouldn't be used as a mechanism to differentiate the callers.
The assumed role is the "effective" principal in this case. If you want the API to behave differently based on the user, then please use unique role for each user.
RoleSessionName is the way to tell each user apart. One cannot create a separate role for each user because each account can only have 1000 roles at most. I think this API call is not meant to be used on client side for authentication purpose. It's only meant to give a role to an already authenticated user on server side.