cfworker / cfworker

A collection of packages optimized for Cloudflare Workers and service workers.
https://cfworker.dev
MIT License
622 stars 55 forks source link

Feature: support caching JWKS in KV #158

Closed hazcod closed 2 years ago

hazcod commented 3 years ago

Would be nice to store the JWKS with a TTL of 1 hour in Cloudflare KV.

jdanyow commented 2 years ago

Great idea. It's out of scope for the @cfworker/jwt lib but you could implement it in your project by taking control of the jwks fetching:

import { getJwks, importKey } from '@cfworker/jwt';

const iss = 'https://example.auth0.com';
const kvKey = 'jwks:' + iss;

let jwks = await NAMESPACE.get(kvKey, { type: 'json' });
if (!jwks) {
  jwks = await getJwks(iss);
  await NAMESPACE.put(JSON.stringify(jwks));
}
await Promise.all(jwks.keys.map(jwk => importKey(iss, jwk)));
hazcod commented 2 years ago

@jdanyow Thank you! This works:

import { getJwks, importKey, parseJwt } from '@cfworker/jwt';

export async function getVerifyToken(event)
{
  let jwt = event.request.headers.get('Authorization');
  if (! jwt) { return null; }

  jwt = jwt.replace('Bearer ', '');

  const issuer = 'https://' + AUTH0_DOMAIN + '/';
  const audience = AUTH0_CLIENTID;

  const kvKey = 'jwks:' + issuer;
  let jwks = await JWKS.get(kvKey, { type: 'json' });

  if (!jwks) {
    console.log('retrieving new JWKS: ' + issuer);

    jwks = await getJwks(issuer);
    await JWKS.put(kvKey, JSON.stringify(jwks), {expirationTtl: 60 * 60});
  }
  else {
    console.log('using cached JWKS: ' + issuer);
  }

  await Promise.all(jwks.keys.map(jwk => importKey(issuer, jwk)));

  const result = await parseJwt(jwt, issuer, audience);

  if (! result.valid) {
    console.log('invalid token: ' + result.reason);
    return null;
  }

  return result.payload;
}