Set HTTP Only cookie with an AWS Authorizer (Cognito or Lambda) using API Gateway V2 - amazon-web-services

I have an existing API Gateway -> Lambda -> DynamoDB RESTful API that is working.
I would like to set up a login system to gate access to this API using Cognito + Cognito (or a custom lambda) Authorizer
I set up Cognito and am able to sign up a user (and also hit a callback URL with a returned code to swap for an access_token and id_token).
My UI is a React app and I am not using Amplify.
Every single tutorial out there suggests you set the callback to your UI (SPA) and parse out the token to use in further API calls. This isn't good because your token is exposed.
I would like to instead set this token in an HTTP-only cookie in the backend, and have it validated when the Cognito (or custom) authorizer is hit.
I also want to check if the token is expired (1 hour) and refresh it with a stored refresh_token, and set it for the user again in the same HTTP-only cookie.
Does Cognito Authorizer do this automatically? And if not, how can I do this in the authorizer so I can avoid duplicated code in each of my lambdas (since the authorizer only validates and then passes the request to the lambda, I don't think you can set the response headers then).
Is this even possible?

It is my understanding that Cognito does not support this functionality currently.
It sounds to me like the best approach might actually be to write a simple authentication/identity micro-service which can be responsible for the generation and management of tokens, which abstracts away Cognito from your front-end application. This micro-service could also be responsible for refreshing tokens.
Then you can pass these tokens from your authentication/identity micro-service to any API Gateway you have hooked up with a Cognito authoriser.

Related

using api gateway with aws cognito for protected routes

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.

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.

AWS Gateway API authentication with users stored in DynamoDB

I'm trying to implement the following scheme:
with Angular SPA on the frontend and Gateway API + lambda functions on the backend
Since my app requires authentication, there will be /auth endpoint checking user credentials (users are stored in DynamoDB) and returning auth tokens (JWT) in case of success. Client will have to send auth tokens with every request to backend, and my lambda functions will have to validate those tokens.
This scheme looks good, but I'm wondering can it be changed somehow so that responsibility to check tokens is moved out of lambda functions (lambdaAction on the picture above)?
I have seen tutorials of using third-party services like Auth0
with authentication taking place in Gateway API, before any lambda functions (see the link for example) But I can't figure out how to use those services with users stored in my own DB.
So, my question, in short: is it possible to use Gateway API with token-based authentication with users stored in my database
The Auth0 example still uses a Lambda function to validate the JWT on each request. API gateway isn't going to validate JSON Web Tokens automatically, you have to provide a Lambda function to do that.
I would look into using the new API Gateway Custom Authorizers feature. This way you can have a single Lambda function that is responsible for validating the JWT for each request. This keeps your authentication code encapsulated in a single function instead of duplicated in every single Lambda function. It also allows you to do authentication in Lambda while the actual API endpoint may be pointing to something other than Lambda.
In addition to Mark B's answer.
I wrote a generic Custom Authorizer that works with Auth0 a the 3rd-party Single-Sign-On service.
As part of the Authentication, it will optionally store the Auth0 user data to DynamoDB.
Library can be found here
https://github.com/jghaines/lambda-auth0-authorizer