I am trying to test authenticated API gateway endpoint from rest client. How to I generate/set the "AWS_IAM" authorization headers when making the request ?
You can use Cognito with a "public" pool id, then attach role to the Cognito pool id, the role being accessing your API GATEWAY
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'REGION:YOUR_POOL_ID',
});
Use AWS STS to get temporary credentials with limited privileges. After that you can use API Gateway with AWS_IAM authentication
The generated SDK accepts AMI credentials, you have to initiate the client with the one you got from STS:
var apigClient = apigClientFactory.newClient({
accessKey: 'ACCESS_KEY',
secretKey: 'SECRET_KEY',
sessionToken: 'SESSION_TOKEN', //OPTIONAL: If you are using temporary credentials you must include the session token
region: 'eu-west-1' // OPTIONAL: The region where the API is deployed, by default this parameter is set to us-east-1
});
NB: Put strictly minimum roles on your pool, that is a publicly available id, every body can use it to get a temporary or a fixed (to track users across devices) user_/app_ id.
Update April 2016:
For Christine comment's: Documentation on how to use STS.
TL;DR: Basically after your Identity provider calls you back (Google, in my case), you will have a Token (OpenID, in my case), just feed it to STS:
AWS.config.credentials = new AWS.WebIdentityCredentials({
RoleArn: 'arn:aws:iam::<AWS_ACCOUNT_ID>:role/<WEB_IDENTITY_ROLE_NAME>',
ProviderId: 'graph.facebook.com|www.amazon.com', // Omit this for Google
WebIdentityToken: ACCESS_TOKEN
});
You'd have to replicate API Gateway AWS v4 request signature logic to be able to do that. Ideally you should look at the the generated Javascript/Java SDK for your API to get an idea on how these request signatures get calculated. I suggest you turn the authentication off for your testing requests.
Related
My users login to my application through a microservice that connects to cognito (the request is proxied via API gateway)
They get a session token.
Once logged in, they need to put some files to S3.
I want to give them temporary credentials using STS but to call sts.AssumeRoleWithWebIdentity I need a web identity token.
How can I get a web identity token with a session token as input?
I wrote a temporary lambda (node) that returns STS credentials upon logging with a username and password:
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
const cognitoidentity = new AWS.CognitoIdentity();
cognitoidentityserviceprovider.initiateAuth(...) //AuthFlow: 'USER_PASSWORD_AUTH'
cognitoidentity.getId(...)
cognitoidentity.getCredentialsForIdentity(...)
There can be some time between the login and the file upload and I don't want the user to submit user/password each time. There's no AuthFlow accepting a session token either.
I'm guessing the API Gateway could return something useful but I didn't find anything in the docs:
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html#context-variable-reference
A few checks first:
let cognito authenticated user to "masquerade" under an iam role, to do that we use trust relationships, for quick you can reuse the iam role that get assigned to your cognito identity pool.
grant that iam role a policy to access to s3 bucket
Once done:
Run cognitoidentity.getCredentialsForIdentity(...) again, it will go through sts first, thus you don't have to call sts assume role api. If successful, the response should have AccessKeyId, SecretKey and SessionToken. These are expiry aws creds that have access to s3 and will be gone after an hour (unless set). Use them as normal session authentication.
creds = new SessionAWSCredentials(AccessKeyId, SecretKey, SessionToken);
s3Request = CreateAmazonS3Client(creds);
In my React Native App I am using API Keys with AWS AppSync and I want to move to using Cognito or IAM but with no user sign in.
My React Native app that just uses AWS Appsync to read to and write from DyanmoDB.
I initially set up the app to use API keys as it was easier to understand and I'm now attempting to transition to using AWS Cognito or IAM.
To do this in my AWS Console I changed the "Appsync->MyAppAPI->Settings->Default authorisation mode"/"API-level" from "API key" to "AWS Identity and Access Management (IAM)".
I then created an Identity Pool and allowed "Enable access to unauthenticated identities".
My aws-exports file is
// WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.
const awsmobile = {
"aws_appsync_graphqlEndpoint": "https://xxxxx.appsync-api.eu-west-x.amazonaws.com/graphql",
"aws_appsync_region": "eu-west-X",
"aws_appsync_authenticationType": "AWS_IAM",
"aws_appsync_apiKey": "xxx-xxxxxxxxxxxxxxxxxxxxxxxxxx",
};
export default awsmobile;
In my App.js file I have attempted to get the identityPoolId to be used and I have created this:
Amplify.configure({
url: config.aws_appsync_graphqlEndpoint,
region: config.aws_appsync_region,
auth: {
type: config.aws_appsync_authenticationType,
apiKey: config.aws_appsync_apiKey,
region: 'eu-west-x',
identityPoolId: 'eu-west-x:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
}
})
When I use the app to read from DynamoDB I get this error
[WARN] 04:51.835 API - ensure credentials error, No Cognito Federated Identity pool provided
I then went back to the Cognito Manage Identity Pool page and changed my identity pool to use Cognito as an authentication source and provided a User Pool ID and an App client id. But I still get the same error.
I am fundamentally missunderstanding something, could you offer any insight?
I have an Identity Pool which has an Authenticated Role. One of the policies on that role is to have Full Invoke Access to API Gateway Endpoints. When I create temporary credentials with AWS.config.credentials.get(), I create an instance of the API Gateway client like so.
AWS.config.region = 'us-east-1';
var newClientCredentials = {
accessKey: accessKey,
secretKey: secretKey
};
apiClient = apigClientFactory.newClient(newClientCredentials);
apiClient.myendpointPost({}, postRequest, requestParams).then(function(result) {console.log(result)});
I've double checked that accessKey and secretKey are set. When I make a call with apiClient to my endpoint, I get an unauthorized response from the Gateway.
My questions:
Is there a way to check that the temporary credentials (accessKey and secretKey) are for the appropriate Authenticated Role?
Is it possible to find out why API Gateway is rejecting these credentials (maybe something I can log to CloudWatch)?
As Ninad pointed out in the comments I forgot to pass in the session as well with the keys. However, one key step that I had missed was that the Authorization on the endpoint I was hitting had to be set to AWS_IAM. I had previously set it to Cognito and pointed to the User Pool, which was not correct.
I have a Cognito User Pool and an API Gateway using Cognito User Pool authorizer. Each user in the pool is assigned to a group with an IAM role. With this setup at hand, the client authenticates with Cognito and then sends ID Token to the API Gateway, which authorizes the request and passes it to a Lambda function. The lambda then executes other services (DynamoDB and S3). My question is which credentials do I need to use in the lambda?
One option is to use ID Token passed to the lambda from API Gateway to get temporary credentials from STS. This basically allows me to use user's group IAM Role to access other AWS resources and would look as follows:
var credentials = new CognitoAWSCredentials(
"us-east-1:...", // Identity pool ID
RegionEndpoint.USEast1 // Region
);
credentials.AddLogin(
"cognito-idp.us-east-1.amazonaws.com/us-east-...", // User pool ID
"eyJraWQiOiJi..." // ID token
);
// use credentials to access AWS services
Another option is to use the execution role assigned to the Lambda function, but it almost feels like this is better suited for other scenarios... perhaps those where lambda is executed as part of an event? I also don't seem to understand how to get ahold of those credentials within lambda itself.
Is there something else I'm missing? What's a good approach to take here?
Update
What I had above was almost right, but not quite. First of all, figure out if your users will need unauthorized access. If so, go to your identity pool and enable unauthorized access.
To get access/secret keys for unauthorized users you'd do something like this (Using C# SDK, but the following concepts apply to other languages):
var client = new AmazonCognitoIdentityClient(
new AnonymousAWSCredentials(), // We're making public API calls
RegionEndpoint.USEast1); // Your Region
var identityId = await client.GetIdAsync(new GetIdRequest
{
AccountId = "****", // AWS account ID
IdentityPoolId = "us-east-1:****" // Identity Pool ID
});
var credentials = await client.GetCredentialsForIdentityAsync(new GetCredentialsForIdentityRequest()
{
IdentityId = identityId.IdentityId
});
// Use credentials.Credentials when calling AWS services
The above will issue credentials with permissions defined by the Unauthenticated IAM Role defined in your Identity Pool.
To get credentials for authenticated users you'll do this:
var client = new AmazonCognitoIdentityClient(
new AnonymousAWSCredentials(), // We're making public API calls
RegionEndpoint.USEast1); // Your region
var getIdentityIdRequest = new GetIdRequest
{
AccountId = "****", // AWS Account ID
IdentityPoolId = "us-east-1:****" // Identity Pool ID
};
getIdentityIdRequest.Logins.Add(
"cognito-idp.us-east-1.amazonaws.com/us-east-1****", // User Pool ID
idToken); // ID Token
var identityId = await client.GetIdAsync(getIdentityIdRequest);
var getCredentialsRequest = new GetCredentialsForIdentityRequest()
{
IdentityId = identityId.IdentityId
};
getCredentialsRequest.Logins.Add(
"cognito-idp.us-east-1.amazonaws.com/us-east-1****",
idToken);
var credentials = await client.GetCredentialsForIdentityAsync(getCredentialsRequest);
// Use credentials.Credentials when calling AWS services
You'll need the ID Token for this, so authenticate first and grab the ID token from the response. If the user holding the ID token belongs to a group in Cognito with an assigned IAM Role, the credentials returned from the above code will be for that role. If the user isn't assigned to a group or the group has no role, you'll get credentials for Authenticated User IAM Role assigned to the Identity Pool.
Also, as was suggested by #thomasmichaelwallace, the above can be done either from Lambda or from outside API Gateway/Lambda but only if you're using AWS_IAM Authorizer because it's the only one that accepts secret and access keys for authorization. If you're just using Cognito Authorizer, like i was, you'll need to pass ID token to your lambda and have your lambda gets credentials.
From your example, you're effectively using the "built-in" method for this (Cognito Federated Identities). Arguably you might take this even further and access your resources directly from your application using those credentials (rather than putting API-Gateway + Lambda in the way).
The execution role in Lambda is the role (credentials) that the lambda is invoked with (they do not need to be set, and no new CognitoAWSCredentials needs to be called by you). As you've suggested, this is to limit what a lambda can do (on a per-lambda basis, rather than the invoker).
If Cognito Federated Identities are working for you, then it makes sense for you to use them as they provide a way to use IAM to enforce your application's authorisation layer; and arguably mean that you're responsible for building less. Not all applications fit into this pattern, which is why AWS give you options.
Scenario:
I create an app on Amazon, and use Login with Amazon, which returns an "access_token". Then I run:
AWS.config.credentials = new AWS.WebIdentityCredentials({
RoleArn: 'arn:aws:iam::416942672???:role/???_amazon_role',
ProviderId: 'www.amazon.com',
WebIdentityToken:"?????????"
});
AWS.config.region = 'us-west-2';
dynamodb = new AWS.DynamoDB() dynamodb.listTables({}, function a(error,data){
alert( "error: " + JSON.stringify(error) );
alert( JSON.stringify(data) );
});
When I later run the ListTable function it will return:
error: {"message":"Missing credentials in config","code":"SigningError","name":"SigningError","statusCode":403,"retryable":false}
I found it seems that I have to call AssumeRoleWithWebIdentity. But how can I call it in AWS SDK for JavaScript? Or is there any other process I missed?
The process of Getting Temporary Credentials indeed requires a call to AssumeRoleWithWebIdentity - this low level API call is implied in the higher level AWS SDK for JavaScript credentials provider call new AWS.WebIdentityCredentials() though, see e.g. Class: AWS.WebIdentityCredentials:
Represents credentials retrieved from STS Web Identity Federation
support.
By default this provider gets credentials using the
AWS.STS.assumeRoleWithWebIdentity() service operation. This operation
requires a RoleArn containing the ARN of the IAM trust policy for the
application for which credentials will be given. In addition, the
WebIdentityToken must be set to the token provided by the identity
provider. See constructor() for an example on creating a credentials
object with proper RoleArn and WebIdentityToken values.
Given the error message "Missing credentials in config", you are obviously passing an incorrect WebIdentityToken, which isn't a surprise given you just specified some ????????? placeholders ;) - since you already use Login with Amazon, which returns an access_token, you'll just need to pass the content of that ACCESS_TOKEN instead of those ????????? placeholders as value for WebIdentityToken and should be all set.
For other readers: Section 6. Putting it all together of Configuring Web Identity Federation in the Browser provides an example for retrieving the access token via Facebook Login. As noted there, Other identity providers will have a similar setup step that involves loading the respective SDK, logging in, and receiving an access token - in particular, how to handle Login with Amazon is documented in detail within Getting Started for Web.
Use AWS instance in this way:
var aws = AWS ;
var region = aws.config.region = 'us-east-1';
var cred = new aws.Credentials(<YOUR_ACCESS_KEY_ID>,
<YOUR_SECRET_ACCESS_KEY>, sessionToken = null);
To get ACCESS KEY ID and SECRET ACCESS_KEY go to:
AWS IAM>Users>Security Credentials>Access Keys
Now use aws instance for further operations.