I am wondering, if there are any plans to support signature validation with JSON Web Key:
https://tools.ietf.org/html/draft-ietf-jose-json-web-key-38
Here is an example JWK:
https://authorize.smartplatforms.org/jwk
brianloveswords/node-jws does the actual signing. @awlayton made a node module that injects jwk support into node-jws (and therefore) node-jsonwebtoken.
https://github.com/OADA/node-jws-jwk
Maybe node-jws should incorporate @awlayton's work upstream.
Brightspace/node-jwk-to-pem looks like a nice solution for JWK support.
I ended up using dannycoates/pem-jwk to do JWK with jsonwebtoken.
@RCanine Yep, using it exactly for this use-case, as suggested by the example. Might also check out https://github.com/Brightspace/node-jwk-allowed-algorithms for a better solution than the toString checks jsonwebtoken has to do since the context a JWK provides isn't available.
Are there actual plans to support this? The issue has been open for years and looks quite stale...
This PR could open the door for this, since you would have an async way to load the secret/key based on headers (kid) & payload (issuer).
are there any updates on this? this issue has been open for 4 yrs now 馃槄
I have been working to make JWK a thing and with the introduction of #480 this should be a breeze. Especially if you combine it with either https://www.npmjs.com/package/jwks-rsa or https://www.npmjs.com/package/jwks-client :-)
@ziluvatar : I think this can be closed :-)
As @JacoKoster said this can be implemented using v8.3.0+ passing a function as secretOrPublicKey parameter (see README for more info)
I'm a few years late to the discussion, but I was just searching to figure out how to do this myself, as I was expecting that jsonwebtoken would support jwks out-of-the-box... momentarily forgetting that node doesn't have JWK support... duh. :)
Then I started doing research and looking into things based on the comments here and I figure I'll just sum up a few things.
First, I'm a bit of an odd duck in that I work a lot on small devices (and I'm old school), so writing vanilla js and reducing dependencies is more important to me than to most people. I've also become even more dependency-phobic in the wake of the malicious code that was included with npm's most popular projects this past year.
Although there are many libraries out there that do JWK-to-PEM and PEM-to-JWK, they're kinda half-baked (i.e. only do RSA or only do JWK-to-PEM or only public keys) and have _huge_ dependency chains. Because of that I dug into node's new RSA and ECDSA APIs (added mid-v10) a while back and created tiny, lightweight libs that to do this sort of thing in just a few kilobytes, rather than megabytes (due to node's native APIs and barebones ASN.1 handling):
Example:
// barebones dependencies
var JWT = require('jsonwebtoken');
var Eckles = require('eckles'); // EC support
var Rasha = require('rasha'); // RSA support
// A wrapper that handles JWKs
function verifyWithJwk(jwt, jwk) {
var pem;
if ('EC' === jwk.kty) {
pem = Eckles.exportSync({ jwk: jwk });
} else if ('RSA' === jwk.kty) {
pem = Rasha.exportSync({ jwk: jwk });
} else {
throw new Error("Expected EC or RSA key but got '" + jwk.kty + "'");
}
return JWT.verify(jwt, pem);
}
// Usage example
try {
verifyWithJwk(jwt, jwk);
} catch(e) {
throw e;
}
Example w/ Express:
var JWT = require('jsonwebtoken');
var Eckles = require('eckles'); // EC support
var Rasha = require('rasha'); // RSA support
var request = require('@coolaj86/urequest'); // 0-dependency API-compatible drop-in for request
var express = require('express');
var app = express();
// same verify function as shown above
function verifyWithJwk(jwt, jwk) {
var pem;
if ('EC' === jwk.kty) {
pem = Eckles.exportSync({ jwk: jwk });
} else if ('RSA' === jwk.kty) {
pem = Rasha.exportSync({ jwk: jwk });
} else {
throw new Error("Expected EC or RSA key but got '" + jwk.kty + "'");
}
return JWT.verify(jwt, pem);
}
app.use('/authorized_endpoint', function (req, res, next) {
// Grab the token from the headers
var jwt = (req.headers.authorization||'').replace('Bearer ', '');
// TODO: we'd want to cache keys so we don't fetch them every time
request({ url: "https://example.com/oidc/jwks/", json: true }, function (resp) {
var jwk = resp.body.keys[0];
try {
verifyWithJwk(jwt, jwk);
} catch(e) {
// express will catch this and show the user an error
throw e;
}
// Let the user know the token is invalid
res.send({ verified: true });
});
});
Because they're so small (and more-or-less complete) I vendor them rather than including them as dependencies. So they do get about 20k+ downloads per week, but directly committed into more popular packages like greenlock and rsa-compat that use them.
If you're using the Open ID Connect .well-known/openid-configurations jwks_uri or Auth0's .well-known/jwks.json, or another URL type that follows that format, this is the lightweight solution that will allow you to use both RSA and ECDSA JWKs with jsonwebtoken:
jsonwebtokenvar keyfetch = require('keyfetch');
var jwt = require('jsonwebtoken');
var auth = "..."; // some JWT
var token = jwt.decode(auth, { json: true, complete: true })
if (!isTrustedIssuer(token.payload.iss)) {
throw new Error("untrusted issuer");
}
keyfetch.oidcJwk(
token.header.kid
, token.payload.iss
).then(function (result) {
console.log(result.jwk);
console.log(result.thumprint);
console.log(result.pem);
jwt.verify(jwt, pem);
});
What I like about the jwks-rsa solution is that it is very specific and constructs an RSA public PEM in about the simplest way possible. In that regard it's even lighter than Rasha.js because it handles even fewer cases (and doing the same for ECDSA would not be that hard).
In fact, I'd probably use it myself if it weren't for the dependency on request - it balloons to 5.9M of dependencies across 48 packages. That just seems insane to me for something that can _literally_ be written in ~400 lines of code (actually, it could be much smaller if you didn't need to maintain API compatibility with request as a drop-in replacement).
This module is built on outdated code that has dependencies which are no longer necessary (except for special use cases that you probably don't have).
It also uses axios rather than request, which is a lot better in the sense that it has very few dependencies (mostly nearly one-liners that could have reasonably been copied rather than depended on). It's still pretty heavy, but much less of a security risk because there are fewer hands in the pot.
Hi @solderjs, AJ,
I can see you're the author of rasha and eckles, i've recently looked into using those for a light-on-the-dependencies JOSE replacement for https://github.com/cisco/node-jose dependency for my own projects (oidc client and provider) I am building that's purely for recent node versions with KeyObject support where I don't intend to maintain javascript fallbacks (such as node-jose does with forge).
// Rant alert
Here's my gripe with the current jwk/jose/crypto node ecosystem.
1) we share this one, everything jwk and jose related is half baked and super heavy on dependencies. This is mostly due to the design of these packages that aim to please everyone, they bundle fallbacks, js implementations for crypto etc. and they lack even the basic EC/RSA key support defined in JWA
2) until 11.6.0 node required a PEM certificate was passed in, with every crypto operation the certificate was parsed in C land, again and again. Introduction of KeyObject is a massive gain for us using crypto with a finite set of keys. (x2 in ops/sec)
Now on to supporting JWKs with node-jsonwebtoken, I'm all for it for the convenience and for one-time jwk use. BUT, knowing what the cost of js jwk <> asn1 <> pem is, followed up by re-parsing the PEM in openssl this adds up to a lot of time not doing the actual operation and just preparing keys which makes me think i'm better off just using the PEM or in the future an already parsed KeyObject.
ad rasha and eckles,
I took a stab at those packages, very impressive result, much 鉂わ笍 for the work you've put in. I wanted to use them for the pure node jose package because of the lack of dependencies they have, but alas
1) eckles does not support P-521 and we'd have a hard time explaining developers which operations they can do with JWK and which they can't
2) i'd always favor requiring eckles and rasha rather then bundling them in -> convenience and maintainence effort
3) there's on-install telemetry bundled with them, i fully understand its purpose and 100% respect it, but it would raise not one eyebrow for a project jsonwebtoken
4) would you expose a benchmark comparing rasha and eckles with other packages? (namely https://www.npmjs.com/package/@trust/keyto and other similar ones)
The same would apply to jsonwebtoken, @ziluvatar, thoughts?
I'd also separate JWKS and JWK for the sake of this issue, bundling request or similar just for a subset of uses doesn't seem alright. The approach for JWKS would be that the fetching is done OOB and then passed to a keystore like object that can be then passed in to the verify operation. But I digress.
@panva I could add P-521 fairly easily, but is there a significant portion of developers actually using it?Only P-256 and P-384 are actually part of the NIST spec. P-521 is not.
Adding support would merely require adding another header match in the code and I think the byte length is handled differently because it's odd rather than even. Duplicates some code, but not that big of a deal.
I've also just published https://www.npmjs.com/package/keyfetch, which handles fetching from OIDC and Auth0 JWKs URLs and converting to PEM.
I could be talked out of the telemetry if it really made that much difference. npm stats are very limited (and inaccurate). One of the surprising things I learned about some of my projects once I added telemetry was just how many people use node on Windows.
Lastly, why are you using node if performance on conversion operations is so critical? Why not use Go or Rust?
Lastly, why are you using node if performance on conversion operations is so critical? Why not use Go or Rust?
The choice of language of our customers or even internally in Auth0 varies, but if someone's to use node.js I believe they should be aware they can get way more out of their systems if they just don't convert key formats over and over and use KeyObjects, and the library API design should reflect that to to lead developers to understand the benefits and discourage the use of PEM/JWK inputs for a fairly static key.
node-jws/node-jwa just released with keyobject support, i plan on updating jsonwebtoken with its support as well, plus updating the documentation to point all of the above out
I could be talked out of the telemetry if it really made that much difference. npm stats are very limited (and inaccurate).
+1 on getting rid of it for rasha and eckles, as i wrote to you some weeks ago in an email, i believe you're packing these as a dependency in your other packages so you get much needed telemetry out of the box of those, don't you?
That being said in order for us to pack it with jsonwebtoken and myself with my library (to be published) P-521 would be much appreciated just for completeness sake, as these are server side node libraries a little extra footprint will be far outweighed by the fact there's 0 dependencies, altho maybe they could share some of the internals if they're the same ;) just sayin.
Is this still going to be implemented? I'm currently using https://github.com/Brightspace/node-jwk-to-pem for the meantime.
@root/keypairs is perhaps a more optimal solution, but requires node v10.12+.
@panva: this version does not have telemetry.
A brief introduction to the APIs:
// generate a new keypair as jwk
// (defaults to EC P-256 when no options are specified)
Keypairs.generate().then(function(pair) {
console.log(pair.private);
console.log(pair.public);
});
// JWK to PEM
// (supports various 'format' and 'encoding' options)
return Keypairs.export({ jwk: pair.private, format: 'pkcs8' }).then(function(
pem
) {
console.log(pem);
});
// PEM to JWK
return Keypairs.import({ pem: pem }).then(function(jwk) {
console.log(jwk);
});
// Thumbprint a JWK (SHA256)
return Keypairs.thumbprint({ jwk: jwk }).then(function(thumb) {
console.log(thumb);
});
// Sign a JWT (aka compact JWS)
return Keypairs.signJwt({
jwk: pair.private
, iss: 'https://example.com'
, exp: '1h'
// optional claims
, claims: {
, sub: '[email protected]'
}
});
By default ECDSA keys will be used since they've had native support in node
_much_ longer than RSA has, and they're smaller, and faster to generate.
More at https://git.rootprojects.org/root/keypairs.js/src/branch/master/README.md
Most helpful comment
I'm a few years late to the discussion, but I was just searching to figure out how to do this myself, as I was expecting that
jsonwebtokenwould supportjwks out-of-the-box... momentarily forgetting that node doesn't have JWK support... duh. :)Then I started doing research and looking into things based on the comments here and I figure I'll just sum up a few things.
First, I'm a bit of an odd duck in that I work a lot on small devices (and I'm old school), so writing vanilla js and reducing dependencies is more important to me than to most people. I've also become even more dependency-phobic in the wake of the malicious code that was included with npm's most popular projects this past year.
JWK-to-PEM (RSA, ECDSA)
Although there are many libraries out there that do JWK-to-PEM and PEM-to-JWK, they're kinda half-baked (i.e. only do RSA or only do JWK-to-PEM or only public keys) and have _huge_ dependency chains. Because of that I dug into node's new RSA and ECDSA APIs (added mid-v10) a while back and created tiny, lightweight libs that to do this sort of thing in just a few kilobytes, rather than megabytes (due to node's native APIs and barebones ASN.1 handling):
Example:
Example w/ Express:
Because they're so small (and more-or-less complete) I vendor them rather than including them as dependencies. So they do get about 20k+ downloads per week, but directly committed into more popular packages like greenlock and rsa-compat that use them.
OIDC & Auth0 Key Fetching
If you're using the Open ID Connect
.well-known/openid-configurationsjwks_urior Auth0's.well-known/jwks.json, or another URL type that follows that format, this is the lightweight solution that will allow you to use both RSA and ECDSA JWKs withjsonwebtoken:jsonwebtokenjwks-rsa
What I like about the
jwks-rsasolution is that it is very specific and constructs an RSA public PEM in about the simplest way possible. In that regard it's even lighter than Rasha.js because it handles even fewer cases (and doing the same for ECDSA would not be that hard).In fact, I'd probably use it myself if it weren't for the dependency on
request- it balloons to 5.9M of dependencies across 48 packages. That just seems insane to me for something that can _literally_ be written in ~400 lines of code (actually, it could be much smaller if you didn't need to maintain API compatibility with request as a drop-in replacement).jwks-ecdsa
This module is built on outdated code that has dependencies which are no longer necessary (except for special use cases that you probably don't have).
It also uses
axiosrather thanrequest, which is a lot better in the sense that it has very few dependencies (mostly nearly one-liners that could have reasonably been copied rather than depended on). It's still pretty heavy, but much less of a security risk because there are fewer hands in the pot.