using api gateway with aws cognito for protected routes - amazon-web-services

So I'm going to put a public facing API up using AWS API Gateway, where I'll have back end lambda resources that handle the logic for each route (decoupled microservice).
What should I be storing in the JWT? Currently, I've disabled all read attributes, so the token only contains cognito:username, where in my database I will store this as the user id for each user. My understanding is that once a JWT is properly generated, I can use Cognito as an authorizer with API Gateway, and then once the token JWT details are received at the lambda layer, all I need to do is use the cognito:username key to lookup the user profile in my database.
Should I be implementing any other checks in the backend, or is it safe to rely on API gateway to pass the authenticated request?
Thanks!

The cognito API Gateway authorizer will only check if the token has not expired and if it belongs to the correct user pool. But since you will be extracting username from the token itself, you should be safe. Just make sure to configure API Gateway to pass Authorization header to the lambda, it does not do this by default.

Related

AWS authenticate multiple apigateway

I have a AWS Apigateway which has two api OrderApi and ItemApi. These api uses Lambda authorizer for athorization. React App gets the AWS jwt token on Login. This token is sent as a header to the ApiGateway which will be validated by the authorizer. I have another Apigateway with one api userApi which provides the information about the user. OrderApi and ItemApi call this api to get the user data. UserApi may or may not be directly called by any client. Now my question is
what type of authentication and authorization is required for UserApi. Is it ok to pass the same Jwt token to the UserApi.
Since UserApi is mostly called by other Api, is authentication required for it.
Is this the right approach for common UserApi. I have other Api which will call to get User data.
It completely depends on your system. If user service is not accessible from outside and you have already authorize the user on 1st gateway then then there is no such need. So, you can avoid the same.

How to Authorize APIGateway calls to a Lambda Function?

I have an API Gateway which is connected to a Lambda Function. And In the Method Request for a particular POST Method, I want some restricted people only to be able to call the Method.
One way I can implement that is by explicitly passing a token in the request body which I can provide to every authenticated user and then checking if a token is present in the method. Also, I saw Authorization : AWS_IAM in the Method Request details.
I am new to AWS and cannot figure out how to call the API with AWS_IAM authorization via an external Application using the URL we get after deploying the API ?
I want some restricted people only to be able to call the Method
One way of doing this is by means of API keys:
API keys are alphanumeric string values that you distribute to application developer customers to grant access to your API.
In your question you wrote about "explicitly passing a token in the request body" but it was not clear if you want to implement such a solution yourself, or use the solution provided by API Gateway (i.e. API keys)
The IAM authentication for API Gateway APIs will require to create IAM group or IAM users for those "restricted people" in your AWS Account. General steps for that are explained here:
Control access for invoking an API
How do I enable IAM authentication for API Gateway APIs?
I suggest to do this using the API Gateway Authorizers and create a Cognito Authorizer as you are already using a token of the Authenticated user you can achieve this by:
Go to your API gateway select Authorizers from the left menu.
Click on create Authorizer.
After clicking create Authorizer you will have the below screen that will give you the ability to add your existing Cognito user pool and add "Authorization" as token source.
After setting up your Authorizer you will be able to use the "idToken" returned by Cognito after an authentication and pass it in your API request as Authorization header(BEARER token).
Click on your Resources in your API Gateway and choose your lambda function and under Method Request you will be able to assign your created authorizer under (Settings -> Authorization).

Authorize AWS API Gateway with either API Key or Authorizer

In AWS API Gateway,
- We can set up a resource to reqiure API Key for access.
- We can also set up another resource to require Authorization (e.g. JWT token, handled via a lambda function or AWS Cognito).
The question: can we configure a resource to be accessible in either of the above two situations? Currently, if we enable "API Key Required" and "Authorization" simultaneously, the request needs both the API Key and the Authorization. We were hoping for it to pass with only one of the two.
Hack/workaround: Create two copies of the same resource, and authorize each separately, one with API Key and the other one with an authorizer.
Let authorizer generate/map the API key for you
You have a Lambda authorizer return the API key as part of
the authorization response. For more information on the authorization
response, see Output from an Amazon API Gateway Lambda authorizer.
Pros:
Single end-point
API key is more for usage plan than authorization. Keep it that way.
Cons:
Authorizer will run on each request. Which cost money
Authentication, Identification, Authorization are intertwined concepts. As I got more educated on Auth, here is my answer:
API Keys are used for project/application identification and authorization
JWT are used for user authentication and authorization.
API Key is on project/application scope and JWT is on user scope. In other words, API Key only identifies the application, not the user of the application.
Accordingly, it makes sense not to authorize the same endpoint with both JWT and API Key as it would reduce the governance granularity for users and applications. But, if you have a usecase that requires that type of authorization, the suggested workaround could work.

Do I need to verify a AWS Cognito token in BOTH Lambda AND as API Gateway?

When using a AWS Cognito attribute from a JWT token in a lambda, do I need to verify the JWT? The Lambda is only triggered by an API Gateway which already verifies the token.
Adding details:
- I'm using Cognito Authorizer in the API Gateway to verify the token.
- The lambda is connected to the API Gateway as proxy.
No you don't need to verify the JWT in backend lambda if protected by a custom lambda authorizer by API Gateway. I would suggest you to use REQUEST based lambda authorizer and attach attributes in the response. So your backend lambda will be able to access attributes in event.requestContext.authorizer['your_attribue'].
This will also enable you to test your Lambda in isolation without needing to get attribute from JWT. You can always mock the event object for unit testing.
I ran into the same conundrum, and was trying to find documented confirmation that, within the Lambda, I wouldn't have to do any validation on my own, and that I can safely rely on the the token / claims being genuine. Unfortunately, nothing in the AWS documentation or the forum posts that I've seen so far has explicitly confirmed this.
But I did find something similar for GCP, and how the API Gateway there validates the JWT. From the GCP documentation:
To authenticate a user, a client application must send a JSON Web
Token (JWT) in the authorization header of the HTTP request to your
backend API. API Gateway validates the token on behalf of your API, so
you don't have to add any code in your API to process the
authentication. However, you do need to configure the API config for
your gateway to support your chosen authentication methods.
API Gateway validates a JWT in a performant way by using the JWT
issuer's JSON Web Key Set (JWKS). The location of the JWKS is
specified in the x-google-jwks_uri field of the gateway's API config.
API Gateway caches the JWKS for five minutes and refreshes it every
five minutes.
So, it seems that within GCP at least, we don't have to do anything, and the API Gateway will handle everything. Even though this is not a confirmation that this is how it works in AWS as well, but the fact that this is how it works in GCP, it gives me some more confidence in assuming that it must be so in AWS too.

Trying to setup user pool authentication for API Gateway

I'm trying to setup security on an API using Cognito user pools. I'm getting back 401 Unauthorized.
The API and User Pools are existing, and I've been using the user pool to log into an application. I'm now trying to secure the API calls.
I created an authorizer on the API,
Then I added the authorizer to one GET method in the API
Then finally I tried to test the API in Postman. I'm using the AWS Signature authorization.
Calling the method returns 401. The method functioned before with no security, and turning off the authorizer makes it work again (without security).
Any ideas what step I'm missing?
The AWS Signature authorization is different than a Custom Authorizer.
The AWS Signature authorization (Postman) requests an AWS AccessKey and SecretKey to authenticate requests. This corresponds to IAM Authentication in API Gateway. The AccessKey and SecretKey are received through IAM.
A Custom Authorizer takes a JWT called #id_token that is issued by your specified Cognito User Pool. To test the validity of the token, go to your custom authorizer and click test, and then copy and paste the token into the text area.
The way to perform the Custom Authorizer authentication is this:
obtain an #id_token from the your user pool by following AWS Configuration
Configure API gateway with a Cognito custom Authorizer with your user pool as the source (Seems that you have done correctly)
Use OAuth 2.0 as Authorization in postman, with your #id_token as the Access Token, Or add the header: Authorization with the value Bearer and the #id_token
Drop a comment if you want me to add the AWS Signature Auth Flow.
For authorization using Postman when using Cognito user pools, chose No Auth. Then add a header Authorization (the value in token source field of your authorizer) and copy the id_token into that header value. I did not have to add anything else besides that to make it work (i.e. no bearer).