AWS ApiGateway Lambda Proxy access Authorizer - amazon-web-services

I´m using an Lambda Proxy and a Cognito User Pool Authorizer in my ApiGateway. In the Lambda function I can access the path etc. variables via the event object. In addition to that I want to access the claims of the authenticated user. In the documentation it is written, that I should use:
context.authorizer.claims.property
But I authorizer is null so I get
Cannot read property 'claims' of undefined
Anyone with an idea?

The accepted answer will work but it is not needed. When using Lambda Proxy Integration you can access the authorizer claims at:
event.requestContext.authorizer.claims
You can try to console.log(event); and see the information you get out of a Lambda Proxy Integration in CloudWatch Logs.

If you are referring to this part of the documentation, $context.authorizer.claims is part of the mapping template of the integration. It is not related to the context argument of the handler.
Using Lambda Proxy integration, you are using the passthrough mapping template. I̶t̶ ̶s̶e̶e̶m̶s̶ ̶w̶h̶a̶t̶ ̶i̶t̶ ̶d̶o̶e̶s̶ ̶n̶o̶t̶ ̶i̶n̶c̶l̶u̶d̶e̶ ̶w̶h̶a̶t̶ ̶y̶o̶u̶ ̶a̶r̶e̶ ̶l̶o̶o̶k̶i̶n̶g̶ ̶f̶o̶r̶ (see edit). You'll probably have to disable Lambda Proxy integration and use something like this in the mapping template:
{
"identity" : {
"sub" : "$context.authorizer.claims.sub",
"email" : "$context.authorizer.claims.email"
}
}
The mapping template "build" the event parameter of the Lambda. So you will be able to access to the parts of your claim via the event parameter.
exports.handler = (event, context, callback) => {
// TODO implement
callback(null, event.identity.email);
};
Note that I slightly modified the documentation example to avoid another confusion about what context can be:
the mapping template variable in API Gateway
the second argument of a handler in Lambda
a key of the event argument in some examples of the documentation <= I renamed it identity
Edit
As pointed out by doorstuck, the information is available using the proxy integration

Ensure you are sending the "Identity Token" as the Authorization header instead of the "Access Token".
Documentation for Identity Token
For example, I am using Amplify and was getting the access token with:
userSession.getAccessToken().getJwtToken() // Wrong
instead of
userSession.getIdToken().getJwtToken() // Correct

Related

How to include context variables in AWS API-EventBridge Integration Detail body

I have an ApiGatewayv2 EventBridge-PutEvents Integration running. The POST body is sent into event bridge as the event Detail.
The Integration request parameters are as below:
{
'Source': 'com.me.api',
'DetailType': 'ApiEvent',
'Detail': $request.body,
'EventBusName': BUS_NAME,
}
If I post {"foo": "bar"} to the API endpoint, I end up with an event in my bus with {"foo": "bar"} as the Detail. So far, all straightforward.
I have also enabled an authorizer on the API GW and I want to pass the context from that authorizer into the event Detail as well.
I'm testing with an IAM authorizer for now, but would like to use Cognito.
I can update the request parameters to change the DetailType to $context.identity.caller and I get my Access Key in the DetailType of the Event.
This isn't what I want, but it shows that I can access these context variables in my integration.
What I want is for that to be in the Detail though - not the DetailType.
So, when I POST {"foo": "bar"} to my API GW I get an event with Detail:
{
"body": {"foo":, "bar"},
"auth": {"user": "AAA1111ACCESSKEY"}
}
But, I can't use anything other than $request.body as the Detail value in the Integration's request parameters.
If I use a Detail like {"body": $request.body} I get an error on saving the integration - Invalid selection expression specified: Validation Result: warnings : [], errors : [Invalid source: {"body": $request.body} specified for destination: Detail]
And, I've tried that with stringified {"body": $request.body} as well.
How can I wrap the POST data in a key like "body" within the event's Detail, and add key/values from the context variables I get from the authorizer.
Or, some other way to inject the authorizer context into my Event. Should I just give up and use a lambda to access the ApiGw context and run PutEvent? Seems defeatist!
This SAM template shows how to do what I wanted using an AWS REST API https://github.com/aws-samples/client-event-sample/blob/6d58777382da148a3ccc1314b0bdf21c46b6378f/template.yaml#L196
The rest api endpoint comes with the additional benefit that we can validate the inbound event json

is the Authorization header passed to lambda function by Cognito Authorizer?

I'm planning to add some authorisation logic to my web app. Users are managed & authenticated by Cognito, and the API is powered by Lambda functions glued together with API Gateway.
Currently, Cognito just validates the user's OAuth token and allows/denies the request.
I'd like to further restrict what actions the user can take within my lambda functions by looking at the user's groups.
Looking at the OAuth token, claims about the groups are in the token body. My question is, does the Cognito Authorizer pass the value of the Authorization: Bearer foo header through to API Gateway and the Lambda handler?
The way I can do something like this:
const groups = getGroupsFromToken(event.headers.Authorization);
if (groups.includes('some group')) {
// let user do the thing
}
else {
callback({ statusCode: 401, body: 'You can\'t do the thing' });
}
It definitely sends through the token on a header for me, also it sends through requestContext.authorizer.jwt.claims which may be more useful to you.
The older api gateways I have always uppercase the header to "Authorization", irrespective of what case the actual header uses. The newer ones always lowercase it to "authorization".
I'd suggest trying:
const groups = getGroupsFromToken(event.headers.Authorization || event.headers.authorization);
I am using lambda proxy integration (what the new APIGW UI is calling lambda integration 2.0), from your callback it looks like you are using it too. If you are using the old lambda integration (1.0 in the new UI) then you need a mapping template.

Use ApiGateway Authorizer to Validate Github Payload Signature (X-Hub-Signature)

I am currently working on a simple api to receive Github event payloads, and I want to validate that they are coming from the correct source. With this I am working to use the hmac signature in the requests header (generated by github using a secret provided by me). To validate the signature, the ApiGateway authorizer requires the signature (X-Hub-Signature), the secret used to generate the signature, and the body of the message. As far as I can tell, Api Gateway does not allow you to pass the body to an ApiGateway Authorizer. Does anyone know a way around this that does not require additional proxy lambdas and s3?
*Note: The requester is the Github Webhook service (not able to add body to header)
Basic ApiGateway Auth Docs:
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html
Here is how you do it.
Pass your content to Authorization header of your incoming request, It will get delivered to your custom Authorizer.
Grab the contents of the from the below attribute,
event.authorizationToken
where event is one of the parameters (1st) passed to lambda,
I currently encrypt and add all the info to that header and gets delivered to the Custom Authorizer lambda.
You can also access additional parameters as below in your custom Authorizer lambda:
var headers = event.headers;
var queryStringParameters = event.queryStringParameters;
var pathParameters = event.pathParameters;
var stageVariables = event.stageVariables;
var requestContext = event.requestContext;
Hope it helps.

Acessing user identity in AWS Lambda

I want to use a lambda function that is only available to members of a Cognito user group. Within that function I want access the identity of the specific user that just called the function. So far, I did not get this to work.
I have created a Cognito group and have prepared an HTML-page with JS that can register, confirm and login users. Among other things, I can extract the identity token.
I also created an API in the API Gateway that is connected to a Lambda function. Setting the Authorization to the Cognito group works great and the function can only be called with a valid identity token.
However, I don't know how and if I can access the identity in the Lambda function this way. The flag "Invoke with caller credentials" can only be set with the Authorization AWS_IAM. This, in turn, requires a different Authorization in the HTTP request, but I don't know what I need to do there. Or am I moving into a completely wrong direction?
In other threads, e.g., https://forums.aws.amazon.com/thread.jspa?threadID=231032, and the documentation I found remarks about setting up a request mapping template, but I found no location in the GUI where I could do anything like that.
I'd appreciate any help on this! If any more information can help, just ask for it and I will provide as much as I can.
I finally got the identity of the caller via Mapping Templates.
To use Mapping Templates via JSON format, you need to add a template "application/json". You can then click on the new entry and provide a specific template. In my case, I used
{
"name": "$context.authorizer.claims.name",
"username": "$context.authorizer.claims['cognito:username']"
}
This adds the attributes name and username to the event object in the lambda call, which contain the content of the attribute name and the cognito username respectively.
Request mapping templates can be found in the GUI under Method -> Integration Response -> Mapping Templates.
A template like:
{"testHeader" : "$input.params().header.get('Authorization')"}
will pass the token to the lambda under 'testheader'.
It may also be worth checking out the context variable (https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html#context-variable-reference) to see if it holds the information you require, although I don't believe it will, unless you move to a Custom Authorizer method of authentication.

Get Cognito user pool identity in Lambda function

I have a Lambda function handling POST requests triggered by the API Gateway. The latter is set up to authorize via a Cognito user pool authorizer. Authorization works - if I pass a user's ID token, the request is processed, if I don't I get a 401.
However, I can't get the authorized user's identity in the Lambda function. All documentation makes me believe that it should be in the context, but it isn't. I can't map it in there, either. What's more, there doesn't seem to be a way to query the user pool for a user given their ID token, either.
Do I need an identity pool to accomplish this? If so, how does that work? And why wouldn't the API gateway automatically pass on the user's identity?
It depends on if you have Use Lambda Proxy Integration selected in the Integration Request for the lambda. If you have it set then all the token's claims will be passed through on event.requestContext.authorizer.claims.
If you are not using Lambda Proxy Integration then you need to use a Body Mapping Template in the Integration Request for the lambda. An example template with the application/json Content-Type is:
"context" : {
"sub" : "$context.authorizer.claims.sub",
"username" : "$context.authorizer.claims['cognito:username']",
"email" : "$context.authorizer.claims.email",
"userId" : "$context.authorizer.claims['custom:userId']"
}
This is expecting that there is a custom attribute called userId in the User Pool of course, and they are readable by the client.
You cannot use the id token against the aws cognito-idp APIs, you need to use the access token. You can however use AdminGetUser call with the username, if your lambda is authorized.
Use the event.requestContext.authorizer.claims.sub to get user's Cognito identity sub, which is basically their ID. This assumes you're using Proxy Integration with API Gateway and Lambda.
Here's a simple example using Node; should be similar across other SDKs.
exports.handler = async (event, context, callback) => {
let cognitoIdentity = event.requestContext.authorizer.claims.sub
// do something with `cognitoIdentity` here
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*"
},
body: JSON.stringify("some data for user"),
};
return response;
};