I have a AWS Lambda function which need to talk to an external API to validate the user using bearer token pass in API request header.
Now I want to store that token in session, so I don't want to call external API every time when user send request again with that token.
So which is a best way to do it with AWS lambda.
Thanks
If this request is coming through API Gateway you should look at using a Customer Authorizer. Rather than storing the token in a session, since Lambda APIs are meant to be stateless, you should validate the token in a Custom Authorizer using the necessary keys. The key(s) would typically be set in an environment variable so you can easily access it and validate the token.
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html
Related
I've been dealing with this for a while. I want to call an AWS_IAM authenticated Lambda Function URL sending an access token (generated by Cognito User pool) as the Authorization header.
I know I can send access token as a request header for API Gateway HTTP or REST API, but I'm not sure if it works for Lambda Functions too.
I couldn't find any documentation about my problem, just this other about Signature V4 authentication method to invoke Lambda Functions URL: https://docs.aws.amazon.com/lambda/latest/dg/urls-invocation.html
The Security and auth model for Lambda function URLs has two AuthType options:
AWS_IAM – Lambda uses AWS IAM to authenticate and authorize requests based on the IAM principal's identity policy and the function's resource-based policy.
NONE – Lambda doesn't perform any authentication before invoking your function.
The Cognito JWT-based access token is not an AWS IAM session token, so cannot sign the request using SigV4. You have a different option for each of the Lambda function URL AuthType options.
To use AWS_IAM, you can use Amazon Cognito identity pools to deliver temporary, limited-privilege credentials which can SigV4 sign the request.
To use NONE, you will need to verify the JSON web token yourself, preferably with a software framework (such as AWS JWT Verify).
We need a verification step for user deletion through AWS Cognito.
Upon requesting delete a verification code should be sent to the users email address (like it's done with sign-up).
All AWS Cognito offers is:
DeleteUser: only needs a access token
AdminDeleteUser: only needs a username
How would you incorporate the verification step for deletion into AWS Cognito?
Side note: We're using Lambda in combination with API Gateway to handle all our requests to Cognito.
This flow is not natively supported by Cognito, meaning, if you want to achieve this, you'll have to implement this flow manually as one (or two) endpoints on your API Gateway and a Lambda which in turn uses the AdminDeleteUser functionality.
Simple example:
GET /user/delete: Create a JWT token, send an email to the user with a link, including a token to verify the deletion request. The token can contain the username and an expiration time. (You can use Amazon SES to send the email).
GET /user/delete?token=verificationToken: Extract the username from the token and execute AdminDeleteUser using the username.
I'm adding custom claims to Cognito's ID token using the "Pre Token Generation" trigger.
Problem
The lambda is triggered, but the issued ID Token doesn't include the claims I added. Am I missing something?
My setup
Using OAuth 2.0 with authorization code flow
My client app sits behind a load balancer (alb). The alb interacts with Cognito to get the Access + ID Tokens in the form of a ALBSessionCookie. Very similar to [0]
To get the ID Token, the client calls a custom endpoint to my backend with the ALBSessionCookie. The backend uses that cookie to return a decoded ID Token to the user. This is the ID Token that I expect should have the custom claims included.
[0] https://www.exampleloadbalancer.com/auth_detail.html
Lambda function (pre-token generation trigger)
Format taken from https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html#aws-lambda-triggers-pre-token-generation-example-1
exports.handler = (event, context, callback) => {
event.response = {
"claimsOverrideDetails": {
"claimsToAddOrOverride": {
"my-custom-claims-namespace": JSON.stringify({
"custom-claim-1": "hello",
"custom-claim-2": "hello",
})
}
}
}
callback(null, event)
}
If I can't make this work with ALB, what are some workarounds? Some ideas:
Call Cognito directly for an ID Token (somehow), hoping that will trigger the lambda to issue a JWT with the custom claims
Call Cognito via AmplifyJS
I have a feeling this is expected behavior, though seems like a limitation. Looking here:
https://www.exampleloadbalancer.com/auth_detail.html
We can see that the following steps occur:
ALB receives JWT (ID token, Access Token)
ALB to send access token
ALB receives user info(claims)
I believe the ALB is then not sending the contents of the Decoded ID token (That were manipulated by the Lambda trigger) back to the backend but instead sends the 'user info(claims)' (returned from the UserInfo endpoint) which are not effected by the Cognito trigger.
Yeah the ALB doesn't work that way, the ID Token that Lambda trigger customizes is the one you get when a user Authenticates. There are a couple of options.
Custom User Attributes
The least invasive IMO if instead of adding these attributes in the Lambda trigger, you could have them as custom attributes in Cognito, these I do believe will be in this token. You can sync these attributes at each successful Authorization. That may meet your requirements.
API GW
You could put an API GW either between your LB and your APP or infront of your LB. The API GW does give you a layer in which you can do all this stuff and more with customizing headers, tokens etc. For example you could have a Lambda Authorizer which reads this access token, and returns a context which you can reference in your integration requests back to your backend. It's a bit more involved and will add at least some latency to your app, although you can safely have a large TTL on your auth response because your LB is already doing Auth and you only want some extra attributes. You could also do a re-design and put this all in API GW and get all the bells and whistles it has but you might not need them.
But yeah probably easiest to use the first option if possible as that won't require you to do a redesign and you will just need to change your attribute names to custom:....
Currently I have a lambda function that is receiving a Google access token. However, in order to receive a cognito ID, AWS Cognito only accepts an ID token, rather than an access token.
Is there anyway I can exchange a Google access token for an ID token? If not, how can I get a cognito identity ID from my access token?
I would try to provide the lambda function with an ID token; however, Alexa is the one calling lambda with the access token, and I can't seem to find a way to configure Alexa to call the lambda function with an ID token instead.
For context, I am trying to get info associated with a cognito ID using Amazon Alexa.
Sorry you can't get an id token from an access token. They provide different functionality.
id token is returned together with access token in the token response. you need to modify your lambda function or create a new one to spit it out from the google token response
e.g. https://developers.google.com/identity/protocols/OpenIDConnect#exchangecode
I have a website that uses AWS Cognito (via Amplify) for user login. The API is on a separate stack that deploys with Serverless.
I am trying to have an API endpoint that can access the current logged-in user's Cognito User Pool data (username, email) if it is available. The only way I've been able to achieve this is by using a cognito user pool authorizer via API Gateway.
Example:
functions:
getMe:
handler: /endpoints/myService.get
events:
- http:
path: /myService
method: GET
cors: true
authorizer:
type: COGNITO_USER_POOLS
authorizerId: ${self:custom.apiGatewayAuthorizerId.${self:custom.stage}}
Where authorizerId is set to the 6-character Authorizer ID found on the AWS Console's API Gateway Authorizers page. However, this blocks all traffic that is not authenticated with Cognito. That isn't what I want, since I have a number of services that should be accessible by both anonymous and logged-in users. I just want to personalize the data for users that are logged-in.
Is there any way to allow traffic and pass the cognito user parameters through the API Gateway to Lambda if they are available?
All resources I've been able to find regarding Cognito + API Gateway + Lambda are specifically about restricting access to endpoints and not layering on data to the requests...
Based on comments above you want Anonymous and Logged-in users pass through same gateway end point ?
You can still use the same setup but remove the authentication from API Gateway and take the logic in your application.
If users try to access your services while being logged in AWS amplify will send through the Authorization header with Id token to API Gateway and API Gateway will pass this header as it is to the application. You will have to check inside your application for this Authorization header and crack open Id token passed to find the user claims/attributes and do your logic. For any other user that doesn't have this token can be considered anonymous.
You still need to Validate the token if you find one in request to make sure it's a valid token and extract claims/Attributes thereafter.