You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
brianloveswords/node-jws does the actual signing. @awlayton made a node module that injects jwk support into node-jws (and therefore) node-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.
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.
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):
// barebones dependenciesvarJWT=require('jsonwebtoken');varEckles=require('eckles');// EC supportvarRasha=require('rasha');// RSA support
// A wrapper that handles JWKsfunctionverifyWithJwk(jwt,jwk){varpem;if('EC'===jwk.kty){pem=Eckles.exportSync({jwk: jwk});}elseif('RSA'===jwk.kty){pem=Rasha.exportSync({jwk: jwk});}else{thrownewError("Expected EC or RSA key but got '"+jwk.kty+"'");}returnJWT.verify(jwt,pem);}
varJWT=require('jsonwebtoken');varEckles=require('eckles');// EC supportvarRasha=require('rasha');// RSA supportvarrequest=require('@coolaj86/urequest');// 0-dependency API-compatible drop-in for requestvarexpress=require('express');varapp=express();// same verify function as shown abovefunctionverifyWithJwk(jwt,jwk){varpem;if('EC'===jwk.kty){pem=Eckles.exportSync({jwk: jwk});}elseif('RSA'===jwk.kty){pem=Rasha.exportSync({jwk: jwk});}else{thrownewError("Expected EC or RSA key but got '"+jwk.kty+"'");}returnJWT.verify(jwt,pem);}app.use('/authorized_endpoint',function(req,res,next){// Grab the token from the headersvarjwt=(req.headers.authorization||'').replace('Bearer ','');// TODO: we'd want to cache keys so we don't fetch them every timerequest({url: "https://example.com/oidc/jwks/",json: true},function(resp){varjwk=resp.body.keys[0];try{verifyWithJwk(jwt,jwk);}catch(e){// express will catch this and show the user an errorthrowe;}// Let the user know the token is invalidres.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.
OIDC & Auth0 Key Fetching
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:
Keyfetch.js: for fetching pure JWKs (ECDSA & RSA) to verify tokens with jsonwebtoken
varkeyfetch=require('keyfetch');varjwt=require('jsonwebtoken');varauth="...";// some JWTvartoken=jwt.decode(auth,{json: true,complete: true})if(!isTrustedIssuer(token.payload.iss)){thrownewError("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);});
jwks-rsa
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).
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 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.
Reacted by Brett Ausmeier, Jaco Koster, Ben Brook, Henri Ståhl, Severin Ibarluzea and Abdurrahman Shofy Adianto
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.
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
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
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
i'd always favor requiring eckles and rasha rather then bundling them in -> convenience and maintainence effort
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
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 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.
// 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)returnKeypairs.export({jwk: pair.private,format: 'pkcs8'}).then(function(pem){console.log(pem);});
// PEM to JWKreturnKeypairs.import({pem: pem}).then(function(jwk){console.log(jwk);});
// Thumbprint a JWK (SHA256)returnKeypairs.thumbprint({jwk: jwk}).then(function(thumb){console.log(thumb);});
Activity
abalmos commentedon Feb 14, 2015
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.
kara-ryli commentedon Jun 23, 2015
Brightspace/node-jwk-to-pem looks like a nice solution for JWK support.
awlayton commentedon Jun 23, 2015
I ended up using dannycoates/pem-jwk to do JWK with jsonwebtoken.
omsmith commentedon Jun 30, 2015
@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
toStringchecks jsonwebtoken has to do since the context a JWK provides isn't available.daviskoh commentedon Mar 29, 2018
Are there actual plans to support this? The issue has been open for years and looks quite stale...
ziluvatar commentedon Apr 5, 2018
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).
daviskoh commentedon May 24, 2018
are there any updates on this? this issue has been open for 4 yrs now 😅
JacoKoster commentedon Jun 26, 2018
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 :-)
ziluvatar commentedon Jun 27, 2018
As @JacoKoster said this can be implemented using v8.3.0+ passing a function as
secretOrPublicKeyparameter (see README for more info)coolaj86 commentedon Jan 28, 2019
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.panva commentedon Feb 20, 2019
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.
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
jsonwebtokenThe same would apply to
jsonwebtoken, @ziluvatar, thoughts?I'd also separate JWKS and JWK for the sake of this issue, bundling
requestor 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.coolaj86 commentedon Feb 23, 2019
@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?
panva commentedon Feb 23, 2019
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
+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
jsonwebtokenand 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.Slyke commentedon Jul 27, 2020
Is this still going to be implemented? I'm currently using https://github.com/Brightspace/node-jwk-to-pem for the meantime.
coolaj86 commentedon Jul 28, 2020
@root/keypairs is perhaps a more optimal solution, but requires node v10.12+.
@panva: this version does not have telemetry.
Usage
A brief introduction to the APIs:
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