UNPKG

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