UNPKG

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