auth0 / node-jwks-rsa

A library to retrieve RSA public keys from a JWKS (JSON Web Key Set) endpoint.
MIT License
836 stars 236 forks source link

cache doesn't work for the expressJwtSecret function #355

Closed yidongw closed 1 year ago

yidongw commented 1 year ago

Describe the problem

I used the following code to fetch the secret from auth0

import jwksRsa from "jwks-rsa"

jwksRsa.expressJwtSecret({
    cache: true,
    cacheMaxEntries: 20,
    cacheMaxAge: 3600000, // 1 hour
    rateLimit: true,
    jwksRequestsPerMinute: 5,
    jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`,
})

The secret returned from auth0 doesn't change that often, so I was hoping that expressJwtSecret would cache the response for me. I even tried to extend the cache time by adding cacheMaxAge: 3600000

However, when I check the performance in sentry, I noticed that for every call comes in, expressJwtSecret still needs to make a call to auth0. This will take 100ms to 700ms to finish and it is now becoming our performance bottleneck.

What was the expected behavior?

For the same JWT, expressJwtSecret should only make a call once every cacheMaxAge. expressJwtSecret would store the response in the cache

Reproduction

import jwt from "express-jwt";
import jwksRsa from "jwks-rsa";

export const jwtCheck = (req: Request, res: Response, next: NextFunction) => {
    return jwt({
      secret: jwksRsa.expressJwtSecret({
        cache: true,
        cacheMaxEntries: 20,
        cacheMaxAge: 3600000, // 1 hour
        rateLimit: true,
        jwksRequestsPerMinute: 5,
        jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`,
      }),
      audience: `${process.env.AUTH0_AUDIENCE}`,
      issuer: `https://${process.env.AUTH0_DOMAIN}/`,
      algorithms: ["RS256"],
    })(req, res, next)
}

Environment

adamjmcgrath commented 1 year ago

Hi @yidongw - thanks for raising this.

Looks like you're creating a new instance of the jwt middleware (and expressJwtSecret) for every request:

export const jwtCheck = (req: Request, res: Response, next: NextFunction) => {
    return jwt({
      secret: jwksRsa.expressJwtSecret({
        ...
      }),
      ...
    })(req, res, next)
}

You should only create one:

export const jwtCheck = jwt({
    secret: jwksRsa.expressJwtSecret({
      ...
    }),
  ...
})
yidongw commented 1 year ago

oh that's why! thank you so much!

adamjmcgrath commented 1 year ago

No problem 👍