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.
Related
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).
I am writing an Ember app where I want to allow users to upload pdf files, and I am using AWS S3 for file storage. Because I don't want to hard code my AWS creds, I am using AWS Cognito to create temporary credentials to authenticate users to S3 when they want to upload/download files. I created an Identity Pool on AWS for the users, and have configured the associated IAM roles for authenticated and unauthenticated users (authenticated users get read only access to one of my S3 buckets). I am using my Rails backend (which uses Devise) as the authentication provider for my identity pool. For my frontend authentication I am using Ember Simple Auth with the provided Devise authenticator, so the user must be logged in in order to request a token. Here are the steps I take to obtain the temporary credentials:
1) Get an AWS Cognito Identity Id and Token from my backend (using get_open_id_token_for_developer_identity method from Ruby AWS SDK on my backend)
2) Create a new CognitoIdentityCredentials object using the Identity Id and Token to obtain temporary credentials, and store the creds in a cookie (using js-cookie).
Code:
var AWS = window.AWS;
AWS.config.region = "us-west-2";
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-west-2:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
IdentityId: response.identity_id,
Logins: {
'cognito-identity.amazonaws.com': response.token
}
});
AWS.config.credentials.get(function() {
var date = new Date(AWS.config.credentials.expireTime);
Cookies.set("cognito_creds", { accessKeyId: AWS.config.credentials.accessKeyId, secretAccessKey: AWS.config.credentials.secretAccessKey, sessionToken: AWS.config.credentials.sessionToken }, { expires: date } );
});
I understand that storing important information such as AWS credentials in a cookie is a big no-no, and is definitely not secure. But keep in mind, I am using HTTPS, the credentials expire after an hour, and the IAM role associated with the credentials only gives read-only access to one of my S3 buckets.
My question is: Is it worth the security risk to store these creds as cookies so that I don't have to get a token from my backend every page refresh, or does this approach leave me too vulnerable?
EDIT: To clarify, the alternate solution that I am thinking of would be to store the credentials as properties on an Ember service on the frontend, and get a token from my backend every time the page reloads, but this doesn't seem very efficient.
This previous question has some similar aspects, it might be worth a read. The short version is, either should be alright. It sounds like you've scoped down the credentials perfectly, so you've limited the blast radius if you opt to save them.
If you want to keep the latency down, it isn't a bad way to go. If you're willing to sacrifice that latency or are concerned with the privacy of what could be read from S3, you could go for getting them remotely.
In the AWS Xamarin SDK docs, the Amazon Cognito Identity API documentation for GetOpenIdTokenForDeveloperIdentity() says in its second paragraph that "You must use AWS Developer credentials to call this API."
Now, the general idea is to try to never expose API secrets & keys in the source code especially if there are other mechanisms available. AWS provides the IAM mechanism so that we can assume a role and then we can define which privileges can be assumed by that role.
But this text mentioned in bold above, does it mean i cannot use IAM roles and/or policies to call those APIs? Does it mean i HAVE TO include my accessKey and secretKey in my application source code?
Edit:
AWS provides the Cognito mechanism through which we can requested temporary credentials via
`credentials = new CognitoAWSCredentials ("IDENTITY_POOL_ID", "REGION_NAME");`
without directly using Developer credentials like the accesskey and secretkey etc.
But this text mentioned in bold above, does it mean i cannot use the credentials obtained via cognito because these credentials were not produced with Developer credentials?
Trying to figure out this new cognito thing.
You can use IAM roles to call this API, same way you would call any other AWS sigv4 API. Purpose of the text is to emphasize that unlike other Cognito unauth APIs (getId, getOpenIdToken, getCredetialsForIdentity) you will need to call GetOpenIdTokenForDeveloperIdentity from your server and with AWS credentials. These credentials can be obtained with IAM user or IAM roles.
Edit:
Cognito is meant for vending credentials on the client side applications, for eg: Mobile apps. Typically credentials are vended after federating with social identity providers, eg: Facebook, google, Amazon etc.
The API 'GetOpenIdTokenForDeveloperIdentity' was introduced to allow you to federate with Cognito with your own authentication system. If you don't have your own authentication system, you should not be using this API. This API is meant to be called from your server side application. Of course you can use Cognito to get the credentials on server side and call GetOpenIdTokenForDeveloperIdentity, but that's not what Cognito is meant for. Simpler will be to just use credentials with help of IAM on server side.
I try to set up a test API with AWS API Gateway, Lambda and Cognito so secure the access. Since I am new to the AWS world, I am not sure how can I create a "logged in" post request to the AWS service with for example the request library
I guess on the client side I first have to log in via Cognito and the AWS Api and then use the informations I get to create a signed request like it is described here:http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html ?
If I am on the wrong path, please tell me and point me in the right direction :)
Preface:
I will explain the flow of Google+ integration with Cognito, it goes almost the same for others. I will use node.js (javascript), after that you can have your users authenticated from Google and authorized by IAM and Cognito to access API Gateway.
Cognito is a federated login service, it offers you to sync "configuration" of your mobile/web app. First you have to setup cognito with an identity provider, say Google+ for example. for that:
Create a Google app in your Developer console
Create a pool in cognito and add google as a provider, configure your pool with the policies and the roles (services you want to give your users access to, in this case only API Gateway).
In your web/mobile app, show the user Google+ Signin button, after the user clicks on it, google will call back a callback url with code parameter code, use that token
Use this code to get a Cognito identity for your user, in this case We trust Google:
var params = {
IdentityPoolId: setting.POOL_ID,
Logins: {
'accounts.google.com': google_token // Coming from Google OAuth2
}
}
// Get Id from Cognito
cognitoIdentity.getId(params, resolverFunction);
Get IAM temporary credentials forthat Identity IdentityId, your Google authenticated user:
var params = {
IdentityId: IdentityId,
Logins: {
'accounts.google.com': google_token // Coming from Google OAuth2
}
}
cognitoIdentity.getCredentialsForIdentity(params, resolverFunction)
Your user is now authenticated with Google and have the authorization from the IAM service (through the roles/policies you attached to your Cognito pool).
In your API Gateway, activate the IAM authorization, and use the Credentials you got from point 7.
Use the Accesskey, secretKey and the token to sign every request you make for your API built on top of API Gateway, you can you use this lib: aws-v4-sign-small
Quick notes and headsup:
All of this is Asynchronous actions, so If you are in node js, it is way better to use Promises (ES6 or Bluebird).
Pay super attention to the roles you attached (acces to dynamodb document or S3 file, etc. read more about IAM it is suer helpful and you can do a fine grained authorizations)
Hope it is clear or at least it gives you a direction to start with.
One of the benefits of using API Gateway is that you can automatically generate SDKs for your API, which easily integrate with Cognito credentials. This saves you from the trouble of implementing SigV4 auth yourself.
Here are a couple of simple examples using Cognito credentials with a generated JavaScript SDK:
https://github.com/rpgreen/aws-recipes/blob/master/app/index.html
https://github.com/awslabs/api-gateway-secure-pet-store
Cheers,
Ryan
As Ryan mentioned, the best way to do this is via the API Gateway SDK. The downside to using this stack is that it becomes harder to integrate with off the shelf front-end tools. You can no longer make direct request to your REST end-points, you will want to go through the SDK.
You definitely lose some ease of development because you can't just slap something like ngResource on top of your endpoints and call it a day. You'll have to set up the calls to each of your AWS end points in a service layer yourself.
I have an iOS app that is authenticating using Facebook & Cognito. I am able to make calls to a local ReST service with the following AWS credentials from my iOS app extracted from Cognito:
accessToken: {a_token}
secretToken: {a_token}
sessionToken: {a_token}
I want to get the current Cognito identity from the AWS credentials so I can use that as a key in my DynamoDB table and handle authorization (I don't want to use IAM roles for this). I know I can directly invoke DynamoDB from the iOS app but I do not want my app directly calling my data storage (in case I want to change data storage, add caching, etc...). Is it possible to get the current Cognito identity from the current AWS credentials?
I do not want to pass the identity id with the request, as is defeats the purpose of passing the tokens.
I do not want to use AWS API Gateway either.
From your credentialsProvider you can call getIdentityId() and in continueWithBlock' you can accesscredentialsProvider.identityId`
I am sorry but there is no way to get an identity id from AWS credentials.
Is there a reason you do not want to call Amazon DynamoDB from the device directly using credentials vended by Cognito Identity? Using IAM roles you can restrict the usage for an identity to be able to write to only their records and this is the approach we recommend currently.
You can refer to our blog about fine grain access with DynamoDB using Cognito.