Securing AWS HTTP APIs with JWT Authorizers

Learn about securing HTTP APIs built on AWS API Gateway using JWT authorization.

Brought to you by John Brennan.

Read on

2 Likes

Please let us know if you have any questions!

Thanks for a great post :+1:
What are your thoughts on the next stage options - Using Cognito Groups/Roles to give finer control access to the APIs. Some users have Get access and some have post? and how could this get surfaced going forward - Visible audits of API access layer?
thanks
Julian

I’m sure @john.brennan will address that once he’s online!

Hi Julian! Those are great questions.

Regarding API access control - once you have your Authorizer configured, you can specify authorization scopes that are required to access the route:

This configures the Authorizer to inspect the scope claim inside the JWT. More information on that is available in the JWT Authorizer docs. If you’re using Cognito as your identity provider, custom scopes can be added to your access tokens by adding a Resource server to your User Pool.

As far as Cognito Groups/Roles go, JWT Authorizers don’t treat access tokens from Cognito any differently than those originating from other identity providers, so there’s no configuration options for leveraging Cognito groups/roles in the Authorizer configuration (unlike API Gateway Rest APIs, which can be configured to use Cognito User Pools as an Authorizer.)

That said: access tokens from Cognito do include Groups as a custom claim in the token payload in the cognito:groups property, which could be accessed within your Lambda from the context object. I haven’t tested this yet, but I believe you could get the groups using context.authorizer.claims['cogito:groups']. Using this, you could manually check the access token’s associated groups inside your Lambda implementation.

To your question about logging: HTTP APIs can write access logs to CloudWatch. If there are specific JWT claims you want to add to the logs, they’re available as logging variables. That should provide visibility into the access layer of your HTTP API.

Hope that answered your questions - glad you enjoyed the article!

Hi John

Really useful tutorial which I am trying to combine with Holly’s tutorial using within Vuejs. I got everything working with the JWT tokens and get make queries using curl and Postman.

However I have run into a problem with using CORS in the new AWS API Gateway HTTP API.

I have enabled it and set the Access-Control-Allow-Origin to * however Holly’s VueJS app keeps saying that there isn’t that appropriate header.

If I check the response headers and i can confirm that header hasn’t been applied.

Any ideas how I can get this to work?
Thanks

This was a great post, I was looking for a step by step tutorial and this checked all the boxes.
Thanks

2 Likes

Glad that you liked it @kcooper! Huge kudos @john.brennan!

Hi @tim.martin! I’ve just run through Holly’s tutorial and combined it with mine and I’ve hopefully got an answer for you.

I believe that the reason that the app doesn’t automatically work has to do with CORS preflight requests. Axios sends an OPTIONS request to the API endpoint prior to the actual GET request to check that CORS is understood and the GET request will be allowed. There were a few changes I had to make to my HTTP API configuration to make preflight requests respond successfully and thus allow the API to be called from JavaScript applications:

  1. In the switch statement inside the Lambda handler function, I added a case for OPTIONS requests that returns a 200 response with an empty body.
  2. In addition to the GET and POST routes to wish-list-service in the HTTP API configuration, I added an OPTIONS route that connects to the Lambda integration, but isn’t connected to the JWT Authorizer:
  3. I set the following configuration options in the CORS settings for the API:

This tells the API to allow GET and POST requests from any origin and to allow those requests to include the authorization header.

One more note: I did also recreate the Express API from Holly’s post as an HTTP API, and setting up the Authorizer was a little more complex, as there were nested routes involved. I had to set up the JWT Authorizer on GET requests and allow OPTIONS requests to bypass the JWT Authorizer - here’s a screenshot of the Authorizers configuration in the API.

I hope that all makes sense and helps you get everything working!

1 Like

Hello,

I have a question regarding scopes. I followed the article and got everything working, however, when I assign custom permissions to my API, these get added to the permissions claim, not to the scopes claim.

Because of this I’m unable to use the scopes functionality with Amazon HTTP API Gateway to limit access to a particular resource by scope. Is there a workaround to this?

Hi, I am new to Auth0.
I created an application inside a tenant that uses a Database Connection and I got the SPA example to with the sdk to login/logout just fine.
My next task was to integrate the login so I can secure my API gateway on AWS. I read this article and got it to work using the Custom API just as you described in this article. However, I don’t know how I can use my SPA auth to object the necessary token from the custom api to call my AWS gateway.
I am missing something here. Appreciate any help and direction you can give me.

Thanks

Hi @kamrankhan, sorry for the delayed response! I tested this out, but my scopes get added to the scope claim as expected. Here are the steps I had to take from the tutorial as described:

  1. Add the custom:permission scope to the JWT Authorizer (same as in your screenshot) and to the ‘Permissions’ Tab on the “AWS JWT Authorizer” API:
  2. I had to re-authorize using the API in my “AWS JWT Authorizer (Test Application)” App in Auth0 to grant the custom:permission scope to the app (this is needed for M2M applications; if you’re using a web app or SPA you can request the custom:permission scope during the authorization step):
  3. After that, the auto-generated access token in the “Test” section of the API is updated to include the scope claim including the custom:permission scope:

    (The screenshot comes from jwt.io, for easy decoding of JWTs)

Were there any additional settings that you changed, either on the Application or API side in your Auth0 Dashboard?

Hi @testiam! If I understand your question correctly, you’re looking to use the access token you receive after logging in from an SPA to call your secured API Gateway? (If this isn’t correct, feel free to let me know!)

If you followed an SPA example from our documentation, I’m assuming that your SPA is using auth0-spa-js. If this is the case, you’ll need to add your Auth0 API Audience to the Auth0 configuration. Here’s how it would work in the vanilla JS example I linked in the beginning of this paragraph:

  1. Add the API audience (this is https://auth0-jwt-authorizer in the JWT Authorizer article) to the auth0_config.json file:
{
  "domain": "{DOMAIN}",
  "clientId": "{CLIENT_ID}",
  "audience": "{API_AUDIENCE}"
}
  1. Wherever you’re calling createAuth0Client in your code, you’ll need to pass the audience in as an option:
// ..
const configureClient = async () => {
  const response = await fetchAuthConfig();
  const config = await response.json();

  auth0 = await createAuth0Client({
    domain: config.domain,
    client_id: config.clientId,
    audience: config.audience, // ADD THIS LINE
  });
};

When you log in, you should now be prompted to allow access to your AWS JWT Authorizer API. If you allow it, the access token you get back should now contain the proper claims that allow it to be used in requests to your API Gateway. You’ll need to manually set the Authorization header with the access token:

const accessToken = "...";
const apiGatewayURL = "https://[SUBDOMAIN].amazonaws.com/default/wish-list-service";
const data = {};

fetch(apiGatewayURL, {
  method: "POST",
  headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${accessToken}`
    },
  body: JSON.stringify(data)
});

(Note: I haven’t tested this sample request myself; there may be more configuration to do here to handle Cross-Origin Requests. See an earlier response in this thread for more information about making CORS work on API Gateway.)

Hope that helps!

Thank you for this great post. Really Helpful.
How do we set up “Resource path: /{proxy+}” for HTTP API Gateway V2?.

Hi @mane.badiger! If I understand your question correctly, you’re wondering how to set up a JWT Authorizer on a matched path like /{proxy+}?

When you create the route in the API Gateway dashboard, paths like /{proxy+} also show up in the Authorizer settings. Here’s a screenshot of an example route using a path variable:

You can create routes with path variables exactly the same way named routes are created: click “Routes” in the left menu, click the “Create” button, and set the path and HTTP method(s) allowed for the route. You can then follow the same process as described in the tutorial for adding a JWT Authorizer. More information on creating HTTP API routes, including path variable information, can be found in the API Gateway Docs.

In the Lambda itself, you can access the path variable using the event.pathParameters value. event.pathParameters is an object where the keys match the path variable names and the values are the actual values passed from the URL. So, for example, if you have a route with a path variable like /{proxy+} and the URL requested is /dogs, then event.pathParameters will be {"proxy": "dogs"}. More information about the event payload can be found in the Lambda documentation.

Hope answers your question!

Great article @john.brennan :+1:

How would I extend those principals to have a Blazor client use Auth0 and its ADFS enterprise connector to authenticate, get a JWT token and call my API’s on AWS using HTTP API services you outlined in your article.

Thanks

Terry

Thanks for reporting that @terry.wells!

Let me bring that to the attention of our content team.

Hi @terry.wells

That’s a very broad question that is hard to answer in a forum - you need to architect your IAM system.

For questions like this I suggest Auth0’s professional services (I’m one of the Solution Architects) - our packages will take you from the initial architecting through implementation and then go-live.

You can read more about it here: https://auth0.com/docs/services

Or message me directly and I will connect you.

Thanks!

John

1 Like

Or if you prefer the do-it-yourself approach, you can start here:

John

1 Like

Hi @john.brennan,

I followed your tutorial - thanks for posting. I’m running in to an issue with Auth0 and an AWS Authorizer. I’m just using a simple setup.

I’m getting a 401 Unauthorized message, stating invalid_token, the explanation: “the issuer in the OIDC discovery endpoint metadata does not match the configured issuer”. I’ve tried every combination, and ensuring all CORS things are resolved to no success.

Any ideas? Otherwise I think my only other solution is to create my own Authorizer, which I think will get around the issue.

Any suggestions would be great.

Cheers!!

1 Like