awslabs / aws-jwt-verify

JS library for verifying JWTs signed by Amazon Cognito, and any OIDC-compatible IDP that signs JWTs with RS256, RS384, and RS512
Apache License 2.0
598 stars 43 forks source link

[QUESTION] Why does the library fail to fetch the well known jwks file? #72

Closed eugenserbanescu closed 2 years ago

eugenserbanescu commented 2 years ago

Question How can we help you?

The .verify() function fails to fetch https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json It times out after 1500 ms. (I removed the pool Id and the region on purpose from the url) It's currently running in a lambda in a subnet and the security group has an open egress (so it can reach out anywhere) The url works when I try and open it from a browser and unfortunately the error message doesn't really give me much else to go on with.

Versions Which version of aws-jwt-verify are you using? ^3.0.0 Are you using the library in Node.js or in the Web browser? NodeJS 14.x If Node.js, which version of Node.js are you using? (Should be at least 14) - Lambda aws managed (NodeJS 14.x) If Web browser, which web browser and which version of it are you using? No If using TypeScript, which version of TypeScript are you using? (Should be at least 4) No

hakanson commented 2 years ago

Can you try a test in that Lambda function to retrieve the URL directly?

The logic happens in https-node.ts and uses https.request to make the call. I am curious if using the library directly has the same issue. It might also be worth testing https.request for another URL you know works. This will allow us to isolate if this is a aws-jwt-verify problem, a node https problem, or some config problem with that VPCs network routing.

eugenserbanescu commented 2 years ago

I was on a chat with support as well and they suggested that it might be connectivity issues as well. I'll give it a try now and post my findings here :) thanks!

eugenserbanescu commented 2 years ago

Must be something Lambda specific. It failed when I tried to fetch the jwks url from my function code using fetch. Also tried to fetch https://google.com unsuccessfully.

However I successfully made the call using curl from am EC2 instance in the same vpc & subnet. This suggest to me that it's a configuration issue with my lambda. :) I'll close the issue since it's unlikely to be related to the aws-jwt-verify code

eugenserbanescu commented 2 years ago

Just to confirm: It was a lambda specific issue indeed: Lambdas can not receive public IPs (even if the ENI is created in a public subnet) and therefore cannot reach out to the internet.

To connect to the internet from a lambda, it needs to be in a private subnet with a NAT in order to connect to the internet directly.

diegonc commented 2 years ago

I'm verifying Cognito JWTs (through the CognitoJwtVerifier object) outside the context of AWS (from my local backend instance) with a slow internet and I can do nothing about it. Sometimes it works, sometimes it doesn't :confused:

Is there some way to increase this 1500ms timeout?

ottokruse commented 2 years ago

@diegonc like so:

import { CognitoJwtVerifier } from "aws-jwt-verify";
import { SimpleJwksCache } from "aws-jwt-verify/jwk";
import { SimpleJsonFetcher } from "aws-jwt-verify/https";

const verifier = CognitoJwtVerifier.create(
  {
    userPoolId: "<your user pool id>",
    tokenUse: "access", // or "id",
    clientId: "<your client id>",
  },
  {
    jwksCache: new SimpleJwksCache({
      fetcher: new SimpleJsonFetcher({
        defaultRequestOptions: {
          responseTimeout: 3000,
        },
      }),
    }),
  }
);

In your case I recommend to do either one of these too, at server start:

  1. https://github.com/awslabs/aws-jwt-verify#explicitly-hydrating-the-jwks-cache
  2. https://github.com/awslabs/aws-jwt-verify#loading-the-jwks-from-file
aureliomarcoag commented 1 year ago

In case anyone's running into this on a Lambda@Edge function, with the verifier instantiated outside the handler, I could get this fixed by immediately awaiting a call to hydrate() (as per Otto's previous message: https://github.com/awslabs/aws-jwt-verify#explicitly-hydrating-the-jwks-cache) i.e.

const VERIFIER = CognitoJwtVerifier.create(...);

// Force JWKS cache hydration
await VERIFIER.hydrate();

export async function handler(event, context) {
...
}
ottokruse commented 1 year ago

That's a nice way! With es modules you can do the top level await like that, which is great in this case.