UNPKG

18.6 kBMarkdownView Raw
1# jose
2
3> "JSON Web Almost Everything" - JWA, JWS, JWE, JWT, JWK, JWKS for Node.js with minimal dependencies
4
5<p align="center"><img src="/img/demo.gif?raw=true"/></p>
6
7## Implemented specs & features
8
9The following specifications are implemented by `jose`
10
11- JSON Web Signature (JWS) - [RFC7515][spec-jws]
12- JSON Web Encryption (JWE) - [RFC7516][spec-jwe]
13- JSON Web Key (JWK) - [RFC7517][spec-jwk]
14- JSON Web Algorithms (JWA) - [RFC7518][spec-jwa]
15- JSON Web Token (JWT) - [RFC7519][spec-jwt]
16- JSON Web Key Thumbprint - [RFC7638][spec-thumbprint]
17- JWS Unencoded Payload Option - [RFC7797][spec-b64]
18- CFRG Elliptic Curve ECDH and Signatures - [RFC8037][spec-okp]
19- secp256k1 EC Key curve support - [JOSE Registrations for WebAuthn Algorithms][draft-secp256k1]
20
21The test suite utilizes examples defined in [RFC7520][spec-cookbook] to confirm its JOSE
22implementation is correct.
23
24Available JWT validation profiles
25
26- Generic JWT
27- OIDC ID Token (`id_token`) - [OpenID Connect Core 1.0][spec-oidc-id_token]
28- OAuth 2.0 JWT Access Tokens (`at+JWT`) - [JWT Profile for OAuth 2.0 Access Tokens][draft-ietf-oauth-access-token-jwt]
29- OIDC Logout Token (`logout_token`) - [OpenID Connect Back-Channel Logout 1.0][spec-oidc-logout_token]
30
31<br>
32
33Have a question about using `jose`? - [ask][ask].
34Found a bug? - [report it][bug].
35Missing a feature? - If it wasn't already discussed before, [ask for it][suggest-feature].
36Found a vulnerability? - Reach out to us via email first, see [security vulnerability disclosure][security-vulnerability].
37
38## Sponsor
39
40[<img width="65" height="65" align="left" src="https://avatars.githubusercontent.com/u/2824157?s=75&v=4" alt="auth0-logo">][sponsor-auth0] If you want to quickly add secure token-based authentication to Node.js projects, feel free to check Auth0’s free plan at [auth0.com/overview][sponsor-auth0].<br><br>
41
42## Support
43
44If you or your business use `jose`, please consider becoming a [sponsor][support-sponsor] so I can continue maintaining it and adding new features carefree.
45
46## Documentation
47
48- [jose API Documentation][documentation]
49 - [JWK (JSON Web Key)][documentation-jwk]
50 - [JWKS (JSON Web Key Set)][documentation-jwks]
51 - [JWT (JSON Web Token)][documentation-jwt]
52 - [JWS (JSON Web Signature)][documentation-jws]
53 - [JWE (JSON Web Encryption)][documentation-jwe]
54
55## Usage
56
57For the best performance Node.js version **>=12.0.0** is recommended, but **^10.13.0** lts/dubnium
58is also supported.
59
60Installing `jose`
61
62```console
63npm install jose
64```
65
66Usage
67```js
68const jose = require('jose')
69const {
70 JWE, // JSON Web Encryption (JWE)
71 JWK, // JSON Web Key (JWK)
72 JWKS, // JSON Web Key Set (JWKS)
73 JWS, // JSON Web Signature (JWS)
74 JWT, // JSON Web Token (JWT)
75 errors // errors utilized by jose
76} = jose
77```
78
79#### Keys and KeyStores
80
81Prepare your Keys and KeyStores. See the [documentation][documentation-jwk] for more.
82
83```js
84const key = jose.JWK.asKey(fs.readFileSync('path/to/key/file'))
85
86const jwk = { kty: 'EC',
87 kid: 'dl4M_fcI7XoFCsQ22PYrQBkuxZ2pDcbDimcdFmmXM98',
88 crv: 'P-256',
89 x: 'v37avifcL-xgh8cy6IFzcINqqmFLc2JF20XUpn4Y2uQ',
90 y: 'QTwy27XgP7ZMOdGOSopAHB-FU1JMQn3J9GEWGtUXreQ' }
91const anotherKey = jose.JWK.asKey(jwk)
92
93const keystore = new jose.JWKS.KeyStore(key, anotherKey)
94```
95
96### JWT vs JWS
97
98The JWT module provides IANA registered claim type and format validations on top of JWS as well as
99convenience options for verifying UNIX timestamps, setting maximum allowed JWT age, verifying
100audiences, and more.
101
102The JWS module on the other hand handles the other JWS Serialization Syntaxes with all their
103additional available features and allows signing of any payload, i.e. not just serialized JSON
104objects.
105
106#### JWT Signing
107
108Sign with a private or symmetric key with plethora of convenience options. See the
109[documentation][documentation-jwt] for more.
110
111```js
112jose.JWT.sign(
113 { 'urn:example:claim': 'foo' },
114 privateKey,
115 {
116 algorithm: 'PS256',
117 audience: 'urn:example:client_id',
118 expiresIn: '1 hour',
119 header: {
120 typ: 'JWT'
121 },
122 issuer: 'https://op.example.com'
123 }
124)
125```
126
127#### JWT Verifying
128
129Verify with a public or symmetric key with plethora of convenience options. See the
130[documentation][documentation-jwt] for more.
131
132```js
133jose.JWT.verify(
134 'eyJ0eXAiOiJKV1QiLCJhbGciOiJQUzI1NiIsImtpZCI6IjRQQXBsVkJIN0toS1ZqN0xob0RFM0VVQnNGc0hvaTRhSmxBZGstM3JuME0ifQ.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6ImZvbyIsImF1ZCI6InVybjpleGFtcGxlOmNsaWVudF9pZCIsImlzcyI6Imh0dHBzOi8vb3AuZXhhbXBsZS5jb20iLCJpYXQiOjE1NTEyOTI2MjksImV4cCI6MTU1MTI5NjIyOX0.nE5fgRL8gvlStf_wB4mJ0TSXVmhJRnUVQuZ0ts6a1nWnnk0Rv69bEJ12BoMdpyPrGa_W6dxU4HFj89F4pQwW0kqBK2-TZ_n9lq-iqupj46w_lpKOfPC3clVc7ZmqYF81bEA-nX93cSKqVV-qPNPEFenb8XHKszYhBFu_uiRg9rXj2qXVU7PXGJAGTzhVgVxB-3XDB1bQ_6KiDCwzVPftrHxEYLydRCaHzggDg6sAFUhQqhPguKuE2gs6jVUh_gIL2RXeoLoinx6gZ72rfovaOmud-yzNIUN8Tvo0pqBmx0s_lEhTlfrQCzN7hZNmV1eG0GDDE-S_CfZhPePnVJZoRA',
135 publicKey,
136 {
137 issuer: 'https://op.example.com',
138 audience: 'urn:example:client_id',
139 algorithms: ['PS256']
140 }
141)
142```
143
144<details>
145 <summary><em><strong>Verifying OIDC ID Tokens</strong></em> (Click to expand)</summary><br>
146
147#### ID Token Verifying
148
149ID Token is a JWT, but profiled, there are additional requirements to a JWT to be accepted as an
150ID Token and it is pretty easy to omit some, use the `profile` option of `JWT.verify` or the
151`JWT.IdToken.verify` shorthand to make sure what you're accepting is really an ID Token meant to
152your Client. This will then perform all doable validations given the input. See the
153[documentation][documentation-jwt] for more.
154
155```js
156jose.JWT.IdToken.verify(
157 'eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJzdWIiOiJmb28iLCJub25jZSI6ImE1MWNjZjA4ZjRiYmIwNmU4ODcxNWRkYzRiYmI0MWQ4IiwiYXVkIjoidXJuOmV4YW1wbGU6Y2xpZW50X2lkIiwiZXhwIjoxNTYzODg4ODMwLCJpYXQiOjE1NjM4ODUyMzAsImlzcyI6Imh0dHBzOi8vb3AuZXhhbXBsZS5jb20ifQ.RKCZczgICF5G9XdNDSwe4dolGauQHptpFKPzahA2wYGG2HKrKhyC8ZzqpeVc8cbntuqFBgABJVv6_9YICRx_dgwPYydTpZfZYjHnxrdWF9QsIPEGs672mrnhqIXUnXoseZ0TF6GOq6P7Qbf6gk1ru7TAbr_ieyJnNWcJhh5iHpz1k3mFz0TyTh7UNXshtQXftPUipqz4OBni5r9UaZXHw8B3QYOnms8__GJ3owOxaqkr1jgRs_EWqMlBNjPaj7ElVaeBWljDKuoK673tH0heSpgzUmUX_W8IDUVqs33uglpZwAQC7cAA5mGEg2odcRpvpP5M-WaP4RE9dl9jzcYmrw',
158 keyOrStore,
159 {
160 issuer: 'https://op.example.com',
161 audience: 'urn:example:client_id',
162 nonce: 'a51ccf08f4bbb06e88715ddc4bbb41d8',
163 algorithms: ['PS256']
164 }
165)
166```
167
168Note: Depending on the channel you receive an ID Token from the following claims may be required
169and must also be checked: `at_hash`, `c_hash` or `s_hash`. Use e.g. [`oidc-token-hash`][oidc-token-hash]
170to validate those hashes after getting the ID Token payload and signature validated by `jose`
171
172</details>
173
174<details>
175 <summary><em><strong>Verifying OAuth 2.0 JWT Access Tokens</strong></em> (Click to expand)</summary><br>
176
177#### JWT Access Token Verifying
178
179When accepting a JWT-formatted OAuth 2.0 Access Token there are additional requirements for the JWT
180to be accepted as an Access Token according to the [specification][draft-ietf-oauth-access-token-jwt]
181and it is pretty easy to omit some. Use the `profile` option of `JWT.verify` or the
182`JWT.AccessToken.verify` shorthand to make sure what you're accepting is really a JWT Access Token
183meant for your Resource Server. This will then perform all doable validations given the input. See
184the [documentation][documentation-jwt] for more.
185
186```js
187jose.JWT.AccessToken.verify(
188 'eyJhbGciOiJQUzI1NiIsInR5cCI6ImF0K0pXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJzdWIiOiJmb28iLCJjbGllbnRfaWQiOiJ1cm46ZXhhbXBsZTpjbGllbnRfaWQiLCJhdWQiOiJ1cm46ZXhhbXBsZTpyZXNvdXJjZS1zZXJ2ZXIiLCJleHAiOjE1NjM4ODg4MzAsImlzcyI6Imh0dHBzOi8vb3AuZXhhbXBsZS5jb20iLCJzY29wZSI6ImFwaTpyZWFkIn0.UYy8vEGWS0cS24giCYobMMy9-bqI45p807yV1l-2WXX2J4UO-eohV_R58LE2oM88gl414c6XydO6QSYXul5roNPoOs41jpEvreQIP-HmegjbWGutktWJKfvoOblE5FjYwjrwStjLQGUzkq6KWcnDLPGmpFy7n6gZ4LF8YVz4dLEaO335hMNVNrmSPSXYqr7bAWybnLVpLxjDYwNfCO1g0_TlFx8fHh2OftHoOOmJFltFwb8JypkSB-JXVVSEh43IOEjeeMJIG_ylWIOxfLLi5Q7vPWgub83ZTkuGNe4KmlQJKIsH5k0yZSshsLYUOOH0RiXqQ-SA4Ubh3Fowigdu-g',
189 keyOrStore,
190 {
191 issuer: 'https://op.example.com',
192 audience: 'urn:example:resource-server',
193 algorithms: ['PS256']
194 }
195)
196```
197
198</details>
199
200<details>
201 <summary><em><strong>Verifying OIDC Logout Token</strong></em> (Click to expand)</summary><br>
202
203#### Logout Token Verifying
204
205Logout Token is a JWT, but profiled, there are additional requirements to a JWT to be accepted as an
206Logout Token and it is pretty easy to omit some, use the `profile` option of `JWT.verify` or the
207`JWT.LogoutToken.verify` to make sure what you're accepting is really an Logout Token meant to your
208Client. This will then perform all doable validations given the input. See the
209[documentation][documentation-jwt] for more.
210
211```js
212jose.JWT.LogoutToken.verify(
213 'eyJhbGciOiJQUzI1NiJ9.eyJzdWIiOiJmb28iLCJhdWQiOiJ1cm46ZXhhbXBsZTpjbGllbnRfaWQiLCJpYXQiOjE1NjM4ODg4MzAsImp0aSI6ImhqazMyN2RzYSIsImlzcyI6Imh0dHBzOi8vb3AuZXhhbXBsZS5jb20iLCJldmVudHMiOnsiaHR0cDovL3NjaGVtYXMub3BlbmlkLm5ldC9ldmVudC9iYWNrY2hhbm5lbC1sb2dvdXQiOnt9fX0.SBi7uNUvjHL9TFoFzautGgTQ1MjyeGUNYHL7inpgq3XgTv6xc9EAKuPRtpixmhdNhmInGwUvAeqDSJxomwv1KK1cTndrC9zAMZ7h657BGQAwGhu7nTm41fWMpKQdiLa9sqp3yit5_FNBmqUNeOoMPrYT_Vl9ytsoNO89MUQy2aqCd-Z7BrNJZH0QycdW6dmYlrmZL7w3t3TaAXoJDJ4Hgl2Itkkkb6_6gO-VoPIdVD8sDuf1zQzGhIkmcFrk0fXczVYOkeF2hNYBuvsM8LuO-EPA3oyE2In9djai3M7yceTQetRa1vwlqWkg_xmYS59ry-6wT44aN7-Y6p0TdXm-Zg',
214 keyOrStore,
215 {
216 issuer: 'https://op.example.com',
217 audience: 'urn:example:client_id',
218 algorithms: ['PS256']
219 }
220)
221```
222
223</details>
224
225#### JWS Signing
226
227Sign with a private or symmetric key using compact serialization. See the
228[documentation][documentation-jws] for more.
229
230```js
231jose.JWS.sign(
232 { sub: 'johndoe' },
233 privateKey,
234 { kid: privateKey.kid }
235)
236```
237
238#### JWS Verifying
239
240Verify with a public or symmetric key. See the [documentation][documentation-jws] for more.
241
242```js
243jose.JWS.verify(
244 'eyJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJqb2huZG9lIn0.T_SYLQV3A5_kFDDVNuoadoURSEtuSOR-dG2CMmrP-ULK9xbIf2vYeiHOkvTrnqGlWEGBGxYtsP1VkXmNsi1uOw',
245 publicKey
246)
247```
248
249#### JWE Encrypting
250
251Encrypt using the recipient's public key or a shared symmetrical secret. See the
252[documentation][documentation-jwe] for more.
253
254```js
255jose.JWE.encrypt(
256 'eyJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJqb2huZG9lIn0.T_SYLQV3A5_kFDDVNuoadoURSEtuSOR-dG2CMmrP-ULK9xbIf2vYeiHOkvTrnqGlWEGBGxYtsP1VkXmNsi1uOw',
257 publicKey,
258 { kid: publicKey.kid }
259)
260```
261
262#### JWE Decrypting
263
264Decrypt using the private key or a shared symmetrical secret. See the
265[documentation][documentation-jwe] for more.
266
267```js
268jose.JWE.decrypt(
269 'eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiRUNESC1FUyIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IkVsUGhsN1ljTVZsWkhHM0daSkRoOVJhemNYYlN2VFNheUF6aTBINFFtRUEiLCJ5IjoiM0hDREJTRy12emd6cGtLWmJqMU05UzVuUEJrTDBBdFM4U29ORUxMWE1SayJ9fQ..FhmidRo0twvFA7jcfKFNJw.o112vgiG_qUL1JR5WHpsErcxxgaK_FAa7vCWJ--WulndLpdwdRXHd9k3aL_k8K67xoAThrt10d7dSY2TlPpHdYkw979u0V-C4TNrpzNkv5jpBjU6hHyKpoGZfEsiTD1ivHaFy3ZLCTS69kN_eVKsZGLVf_dkq6Sz6bWE4-ln_fuwukPyMvjTyaTreLjPLBZW.ocKwptCm4Zn437L5hWFnHg',
270 privateKey
271)
272```
273
274## Detailed Support Matrix
275
276| JWK Key Types | Supported | `kty` value | `crv` values |
277| -- | -- | -- | -- |
278| RSA | ✓ | RSA ||
279| Elliptic Curve | ✓ | EC | P-256, secp256k1<sup>[1]</sup>, P-384, P-521 |
280| Octet Key Pair | ✓ | OKP | Ed25519, Ed448<sup>[1]</sup>, X25519<sup>[1]</sup>, X448<sup>[1]</sup> |
281| Octet sequence | ✓ | oct ||
282
283| Serialization | JWS Sign | JWS Verify | JWE Encrypt | JWE Decrypt |
284| -- | -- | -- | -- | -- |
285| Compact | ✓ | ✓ | ✓ | ✓ |
286| General JSON | ✓ | ✓ | ✓ | ✓ |
287| Flattened JSON | ✓ | ✓ | ✓ | ✓ |
288
289| JWS Algorithms | Supported ||
290| -- | -- | -- |
291| RSASSA-PKCS1-v1_5 | ✓ | RS256, RS384, RS512 |
292| RSASSA-PSS | ✓ | PS256, PS384, PS512 |
293| ECDSA | ✓ | ES256, ES256K<sup>[1]</sup>, ES384, ES512 |
294| Edwards-curve DSA | ✓ | EdDSA |
295| HMAC with SHA-2 | ✓ | HS256, HS384, HS512 |
296| Unsecured JWS | ✓ | none<sup>[2]</sup> |
297
298| JWE Key Management Algorithms | Supported ||
299| -- | -- | -- |
300| AES | ✓ | A128KW<sup>[1]</sup>, A192KW<sup>[1]</sup>, A256KW<sup>[1]</sup> |
301| AES GCM | ✓ | A128GCMKW, A192GCMKW, A256GCMKW |
302| Direct Key Agreement | ✓ | dir |
303| RSAES OAEP | ✓ | RSA-OAEP, RSA-OAEP-256<sup>[3]</sup> |
304| RSAES-PKCS1-v1_5 | ✓ | RSA1_5 |
305| PBES2 | ✓ | PBES2-HS256+A128KW<sup>[1]</sup>, PBES2-HS384+A192KW<sup>[1]</sup>, PBES2-HS512+A256KW<sup>[1]</sup> |
306| ECDH-ES (for all EC keys) | ✓ | ECDH-ES, ECDH-ES+A128KW<sup>[1]</sup>, ECDH-ES+A192KW<sup>[1]</sup>, ECDH-ES+A256KW<sup>[1]</sup> |
307| ECDH-ES (for OKP X25519) | ✓ <sup>via [plugin][plugin-x25519]</sup> | ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW |
308| ECDH-ES (for OKP X448) | ✕ ||
309| (X)ChaCha | ✓ <sup>via [plugin][plugin-chacha]</sup> | C20PKW, XC20PKW, ECDH-ES+C20PKW, ECDH-ES+XC20PKW |
310
311| JWE Content Encryption Algorithms | Supported ||
312| -- | -- | -- |
313| AES GCM | ✓ | A128GCM, A192GCM, A256GCM |
314| AES_CBC_HMAC_SHA2 | ✓ | A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 |
315| (X)ChaCha | ✓ <sup>via [plugin][plugin-chacha]</sup> | C20P, XC20P |
316
317| JWT profile validation | Supported | profile option value |
318| -- | -- | -- |
319| ID Token - [OpenID Connect Core 1.0][spec-oidc-id_token] | ✓ | `id_token` |
320| JWT Access Tokens [JWT Profile for OAuth 2.0 Access Tokens][draft-ietf-oauth-access-token-jwt] | ✓ | `at+JWT` |
321| Logout Token - [OpenID Connect Back-Channel Logout 1.0][spec-oidc-logout_token] | ✓ | `logout_token` |
322| JARM - [JWT Secured Authorization Response Mode for OAuth 2.0][draft-jarm] | ◯ ||
323
324Legend:
325- **✓** Implemented
326- **✕** Missing node crypto support / won't implement
327- **◯** TBD
328
329<sup>1</sup> Not supported in Electron due to Electron's use of BoringSSL
330<sup>2</sup> Unsecured JWS is [supported][documentation-none] for the JWS and JWT sign and verify
331operations but it is an entirely opt-in behaviour, downgrade attacks are prevented by the required
332use of a special `JWK.Key`-like object that cannot be instantiated through the key import API
333<sup>3</sup> RSA-OAEP-256 is only supported when Node.js >= 12.9.0 runtime is detected
334
335## FAQ
336
337#### Semver?
338
339**Yes.** Everything that's either exported in the TypeScript definitions file or
340[documented][documentation] is subject to
341[Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). The rest is to be considered
342private API and is subject to change between any versions.
343
344#### How do I use it outside of Node.js
345
346It is **only built for >=10.13.0 Node.js** environment - including `jose` in transpiled
347browser-environment targeted projects is not supported and may result in unexpected results.
348
349#### How is it different from [`jws`](https://github.com/brianloveswords/node-jws), [`jwa`](https://github.com/brianloveswords/node-jwa) or [`jsonwebtoken`](https://github.com/auth0/node-jsonwebtoken)?
350
351- it supports JWK Key Format for all four key types (oct, RSA, EC and OKP)
352- it is providing Key and KeyStore abstractions
353- there is JSON Web Encryption support
354- it supports all JWS / JWE Serialization Syntaxes
355- it supports the "crit" member validations to make sure extensions are handled correctly
356- it is not only validating the signatures, it is making sure the JWE/JWS is syntactically correct,
357 e.g. not having duplicated header parameters between protected/unprotected or per-recipient
358 headers
359
360#### How is it different from [`node-jose`][node-jose]
361
362`node-jose` is built to work in any javascript runtime, to be able to do that it packs a lot of
363backfill and javascript implementation code in the form of
364[`node-forge`](https://github.com/digitalbazaar/forge), this significantly increases the footprint
365of the module with dependencies that either aren't ever used or have native implementation available
366in Node.js already, those are often times faster and more reliable.
367
368#### What is the ultimate goal?
369
370- **No dependencies**, the moment JWK formatted keys are supported by node's `crypto` the direct
371dependency count will go down from 1 to 0. 🚀
372- Just the API one needs, having used other jose modules for 3+ years I only include what's useful
373
374#### Why? Just, why?
375
376I was using [`node-jose`][node-jose] for
377[`openid-client`](https://github.com/panva/node-openid-client) and
378[`oidc-provider`](https://github.com/panva/node-oidc-provider) and came to realize its shortcomings
379in terms of performance and API (not having well defined errors).
380
381&plus; this was an amazing opportunity to learn JOSE as a whole
382
383[ask]: https://github.com/panva/jose/issues/new?labels=question&template=question.md&title=question%3A+
384[bug]: https://github.com/panva/jose/issues/new?labels=bug&template=bug-report.md&title=bug%3A+
385[documentation-jwe]: /docs/README.md#jwe-json-web-encryption
386[documentation-jwk]: /docs/README.md#jwk-json-web-key
387[documentation-jwks]: /docs/README.md#jwks-json-web-key-set
388[documentation-jws]: /docs/README.md#jws-json-web-signature
389[documentation-jwt]: /docs/README.md#jwt-json-web-token
390[documentation-none]: /docs/README.md#jwknone
391[documentation]: /docs/README.md
392[node-jose]: https://github.com/cisco/node-jose
393[security-vulnerability]: https://github.com/panva/jose/issues/new?template=security-vulnerability.md
394[spec-b64]: https://tools.ietf.org/html/rfc7797
395[spec-cookbook]: https://tools.ietf.org/html/rfc7520
396[spec-jwa]: https://tools.ietf.org/html/rfc7518
397[spec-jwe]: https://tools.ietf.org/html/rfc7516
398[spec-jwk]: https://tools.ietf.org/html/rfc7517
399[spec-jws]: https://tools.ietf.org/html/rfc7515
400[spec-jwt]: https://tools.ietf.org/html/rfc7519
401[spec-okp]: https://tools.ietf.org/html/rfc8037
402[draft-secp256k1]: https://tools.ietf.org/html/draft-ietf-cose-webauthn-algorithms-04
403[draft-ietf-oauth-access-token-jwt]: https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt
404[draft-jarm]: https://openid.net/specs/openid-financial-api-jarm.html
405[spec-thumbprint]: https://tools.ietf.org/html/rfc7638
406[spec-oidc-id_token]: https://openid.net/specs/openid-connect-core-1_0.html#IDToken
407[spec-oidc-logout_token]: https://openid.net/specs/openid-connect-backchannel-1_0.html#LogoutToken
408[oidc-token-hash]: https://www.npmjs.com/package/oidc-token-hash
409[suggest-feature]: https://github.com/panva/jose/issues/new?labels=enhancement&template=feature-request.md&title=proposal%3A+
410[support-sponsor]: https://github.com/sponsors/panva
411[sponsor-auth0]: https://auth0.com/overview?utm_source=GHsponsor&utm_medium=GHsponsor&utm_campaign=panva-jose&utm_content=auth
412[plugin-x25519]: https://github.com/panva/jose-x25519-ecdh
413[plugin-chacha]: https://github.com/panva/jose-chacha