"crypto" package inconsistencies

Hey, In my worker, as a middleware, I'm trying to validate a JWT that is being generated from Auth0. Since I have implemented this check in the express service that's running parallel to the worker, I wanted to reuse as much logic as possible. It's not possible to take advantage of the expressjwt middleware since it uses Express-specific types, so I decided to take their code and adapt it to a Worker middleware, but I have noticed inconsistencies in the crypto package, since I'm using the same config, but I am getting different results. The snippet that I will show is based on https://github.com/auth0/express-jwt/blob/129138815162b00b8017e6944f7fc6f3c5776ded/src/index.ts, but I'll skip the boring parts:
2 Replies
vanpana
vanpanaOP2w ago
// ...
// rest of the middleware

// a custom fetcher is needed since `jsonwebtoken` uses http request underneath
const jwksClientConfig = {
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 10,
jwksUri: `https://${auth.domain}/.well-known/jwks.json`, // this resolves to the same uri as the express service
fetcher: async (jwksUri: string) => {
return fetch(jwksUri).then((res) => res.json());
},
};

const options: Params = {
secret: jwksRsa.expressJwtSecret(jwksClientConfig) as GetVerificationKey,
audience: auth.audience,
issuer: auth.issuer,
algorithms: auth.algorithms,
};

const getVerificationKey: GetVerificationKey =
typeof options.secret === 'function'
? options.secret
: async () => options.secret as jwt.Secret;

// ... other steps such as getting the token from the headers
const decodedToken = jwt.decode(token, { complete: true });
const key = await getVerificationKey(req, decodedToken as jwt.Jwt);
jwt.verify(token, key, options); // There's an error thrown here
// ...
// rest of the middleware

// a custom fetcher is needed since `jsonwebtoken` uses http request underneath
const jwksClientConfig = {
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 10,
jwksUri: `https://${auth.domain}/.well-known/jwks.json`, // this resolves to the same uri as the express service
fetcher: async (jwksUri: string) => {
return fetch(jwksUri).then((res) => res.json());
},
};

const options: Params = {
secret: jwksRsa.expressJwtSecret(jwksClientConfig) as GetVerificationKey,
audience: auth.audience,
issuer: auth.issuer,
algorithms: auth.algorithms,
};

const getVerificationKey: GetVerificationKey =
typeof options.secret === 'function'
? options.secret
: async () => options.secret as jwt.Secret;

// ... other steps such as getting the token from the headers
const decodedToken = jwt.decode(token, { complete: true });
const key = await getVerificationKey(req, decodedToken as jwt.Jwt);
jwt.verify(token, key, options); // There's an error thrown here
The error that is thrown is:
TypeError: CryptoKey is not extractable
at genericExport (.../node_modules/jose/dist/browser/runtime/asn1.js:12:15)
at toSPKI (.../node_modules/jose/dist/browser/runtime/asn1.js:20:12)
TypeError: CryptoKey is not extractable
at genericExport (.../node_modules/jose/dist/browser/runtime/asn1.js:12:15)
at toSPKI (.../node_modules/jose/dist/browser/runtime/asn1.js:20:12)
but I believe the original issue comes even before exporting. The jwks-rsa tries to do const key = await jose.importJWK(jwk, resolveAlg(jwk)); and, internally, it gets to return crypto.subtle.importKey('jwk', { ...jwk }, ...rest); I have traced that the difference between the worker implementation and express implementation comes from here, since the data up to this point is completely the same. Am I doing something wrong or is it a difference in the crypto compatibility layer implementation?
Walshy
Walshy2w ago
it looks like the lib is attempting to extract but isn't setting extractable - https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#extractable that's something that will need to be reported to the lib author

Did you find this page helpful?