0
votes

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.

1

1 Answers

2
votes

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.