Headers in API Gateway Authorization are not been read - amazon-web-services

I've implemented a Lambda Function to the authorization and i'm getting errors in my test: the headers are empty. I could check that printing the headers in the console (Cloudwatch).
This is the beginning of my handler:
public class Authorizer implements RequestHandler<APIGatewayProxyRequestEvent, AuthorizerResponse> {
public AuthorizerResponse handleRequest(APIGatewayProxyRequestEvent request, Context context) {
Map<String, String> headers = request.getHeaders();
System.out.println("headers: " + headers);
String authorization = headers.get("Authorization");
...
And this is the result:
Talking about the Authorizer, I set up this way:
And finally, I have my Api Gateway set up this way:
Method Request
Integration Request
What do I have wrong?
Thanks in advance.

Let me answer your question here.
You are using a TOKEN type Lambda authorizer. This will pass the "Authorization" header value as a token. You can see the below sample Lambda event data -
{
type: 'TOKEN',
methodArn:'arn:aws:execute-api:$AWS_REGION:$ACCOUNT_ID:$API_ID/$STAGE/$RESOURCE/',
authorizationToken: 'allow'
}
You might have to modify your code accordingly to retrieve this token.
Alternatively, you can use a REQUEST-based Lambda authorizer function. This will send input to Lambda authorizer as a combination of headers, query string parameters, stageVariables, and $context variables. You can then use your existing code to retrieve the headers from the request.
As for setting a mapping template and "Authorization" header in Method Request and Integration Request, you don't need that unless you are planning to pass it to the integration Lambda function as well.

Related

Webex Teams Webhook with API Gateway and Lambda

I am using a fairly standard pattern of a Webhook with the called endpoint provided by AWS API Gateway and a backend Lambda.
Webex Teams webhooks allow you to provide a secret which is used to sign the outgoing payload with the resulting hash sent in the 'X-Spark-Signature' header.
I create a webhook and receive the event payload in my Lambda but the hashes do not match. Below is my example code:
def validate(key, raw):
hashed = hmac.new(key, raw, hashlib.sha1)
print(hashed.hexdigest())
return hashed.hexdigest()
key = bytes('somecazYs3Cret', 'UTF-8')
raw = bytes(event['body'], 'UTF-8')
signature = event['headers']['X-Spark-Signature']
if validate(key, raw) == signature:
print('AUTHORIZED')
else:
print('REJECTED')
In API Gateway I am using a Mapping Template as described here to pass the request headers through to my Lambda: https://aws.amazon.com/premiumsupport/knowledge-center/custom-headers-api-gateway-lambda/
When the request payload arrives, all fields including the body are already loaded as a python type dict. so I am trying to serialise the body back to a string to check the hash.
Any help?
This turned out to be the way API Gateway was passing the request payload through to Lambda. Instead of the "Mapping Template" I had to enable the "Use Lambda Proxy integration" feature which passes the original body JSON through as a string.
After enabling this and removing the json.dumps() parts of my code, the hashes validate ok.

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.

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;
};

AWS ApiGateway Lambda Proxy access Authorizer

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