jose 6.0.9 → 6.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/webapi/jwks/remote.js +1 -1
- package/dist/webapi/jwt/decrypt.js +2 -2
- package/dist/webapi/jwt/encrypt.js +2 -2
- package/dist/webapi/jwt/sign.js +2 -2
- package/dist/webapi/jwt/unsecured.js +3 -4
- package/dist/webapi/jwt/verify.js +2 -2
- package/dist/webapi/lib/jwt_claims_set.js +84 -3
- package/package.json +1 -1
- package/dist/types/jwt/produce.d.ts +0 -20
- package/dist/webapi/jwt/produce.js +0 -91
package/README.md
CHANGED
|
@@ -118,10 +118,10 @@ Please note that certain algorithms may not be available depending on the runtim
|
|
|
118
118
|
|
|
119
119
|
| Version | Security Fixes 🔑 | Other Bug Fixes 🐞 | New Features ⭐ | Runtime and Module type |
|
|
120
120
|
| ----------------------------------------------- | ----------------- | ------------------ | --------------- | ------------------------------- |
|
|
121
|
-
| [v6.x](https://github.com/panva/jose/tree/v6.x) |
|
|
122
|
-
| [v5.x](https://github.com/panva/jose/tree/v5.x) |
|
|
123
|
-
| [v4.x](https://github.com/panva/jose/tree/v4.x) |
|
|
124
|
-
| [v2.x](https://github.com/panva/jose/tree/v2.x) |
|
|
121
|
+
| [v6.x](https://github.com/panva/jose/tree/v6.x) | [Security Policy] | ✅ | ✅ | Universal[^universal] ESM[^cjs] |
|
|
122
|
+
| [v5.x](https://github.com/panva/jose/tree/v5.x) | [Security Policy] | ❌ | ❌ | Universal[^universal] CJS + ESM |
|
|
123
|
+
| [v4.x](https://github.com/panva/jose/tree/v4.x) | [Security Policy] | ❌ | ❌ | Universal[^universal] CJS + ESM |
|
|
124
|
+
| [v2.x](https://github.com/panva/jose/tree/v2.x) | [Security Policy] | ❌ | ❌ | Node.js CJS |
|
|
125
125
|
|
|
126
126
|
## Specifications
|
|
127
127
|
|
|
@@ -145,7 +145,8 @@ The algorithm implementations in `jose` have been tested using test vectors from
|
|
|
145
145
|
[sponsor-auth0]: https://a0.to/signup/panva
|
|
146
146
|
[WebCryptoAPI]: https://w3c.github.io/webcrypto/
|
|
147
147
|
[Fetch API]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
|
|
148
|
+
[Security Policy]: https://github.com/panva/jose/security/policy
|
|
148
149
|
|
|
149
|
-
[^cjs]: CJS style `let jose = require('jose')` is possible in Node.js versions where `
|
|
150
|
+
[^cjs]: CJS style `let jose = require('jose')` is possible in Node.js versions where the `require(esm)` feature is enabled by default (^20.19.0 || ^22.12.0 || >= 23.0.0).
|
|
150
151
|
|
|
151
152
|
[^universal]: Assumes runtime support of [WebCryptoAPI][] and [Fetch API][]
|
|
@@ -9,7 +9,7 @@ function isCloudflareWorkers() {
|
|
|
9
9
|
let USER_AGENT;
|
|
10
10
|
if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
|
|
11
11
|
const NAME = 'jose';
|
|
12
|
-
const VERSION = 'v6.0.
|
|
12
|
+
const VERSION = 'v6.0.11';
|
|
13
13
|
USER_AGENT = `${NAME}/${VERSION}`;
|
|
14
14
|
}
|
|
15
15
|
export const customFetch = Symbol();
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { compactDecrypt } from '../jwe/compact/decrypt.js';
|
|
2
|
-
import
|
|
2
|
+
import { validateClaimsSet } from '../lib/jwt_claims_set.js';
|
|
3
3
|
import { JWTClaimValidationFailed } from '../util/errors.js';
|
|
4
4
|
export async function jwtDecrypt(jwt, key, options) {
|
|
5
5
|
const decrypted = await compactDecrypt(jwt, key, options);
|
|
6
|
-
const payload =
|
|
6
|
+
const payload = validateClaimsSet(decrypted.protectedHeader, decrypted.plaintext, options);
|
|
7
7
|
const { protectedHeader } = decrypted;
|
|
8
8
|
if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) {
|
|
9
9
|
throw new JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', payload, 'iss', 'mismatch');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CompactEncrypt } from '../jwe/compact/encrypt.js';
|
|
2
|
-
import {
|
|
2
|
+
import { JWTClaimsBuilder } from '../lib/jwt_claims_set.js';
|
|
3
3
|
export class EncryptJWT {
|
|
4
4
|
#cek;
|
|
5
5
|
#iv;
|
|
@@ -10,7 +10,7 @@ export class EncryptJWT {
|
|
|
10
10
|
#replicateAudienceAsHeader;
|
|
11
11
|
#jwt;
|
|
12
12
|
constructor(payload = {}) {
|
|
13
|
-
this.#jwt = new
|
|
13
|
+
this.#jwt = new JWTClaimsBuilder(payload);
|
|
14
14
|
}
|
|
15
15
|
setIssuer(issuer) {
|
|
16
16
|
this.#jwt.iss = issuer;
|
package/dist/webapi/jwt/sign.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { CompactSign } from '../jws/compact/sign.js';
|
|
2
2
|
import { JWTInvalid } from '../util/errors.js';
|
|
3
|
-
import {
|
|
3
|
+
import { JWTClaimsBuilder } from '../lib/jwt_claims_set.js';
|
|
4
4
|
export class SignJWT {
|
|
5
5
|
#protectedHeader;
|
|
6
6
|
#jwt;
|
|
7
7
|
constructor(payload = {}) {
|
|
8
|
-
this.#jwt = new
|
|
8
|
+
this.#jwt = new JWTClaimsBuilder(payload);
|
|
9
9
|
}
|
|
10
10
|
setIssuer(issuer) {
|
|
11
11
|
this.#jwt.iss = issuer;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import * as b64u from '../util/base64url.js';
|
|
2
2
|
import { decoder } from '../lib/buffer_utils.js';
|
|
3
3
|
import { JWTInvalid } from '../util/errors.js';
|
|
4
|
-
import
|
|
5
|
-
import { ProduceJWT } from './produce.js';
|
|
4
|
+
import { validateClaimsSet, JWTClaimsBuilder } from '../lib/jwt_claims_set.js';
|
|
6
5
|
export class UnsecuredJWT {
|
|
7
6
|
#jwt;
|
|
8
7
|
constructor(payload = {}) {
|
|
9
|
-
this.#jwt = new
|
|
8
|
+
this.#jwt = new JWTClaimsBuilder(payload);
|
|
10
9
|
}
|
|
11
10
|
encode() {
|
|
12
11
|
const header = b64u.encode(JSON.stringify({ alg: 'none' }));
|
|
@@ -58,7 +57,7 @@ export class UnsecuredJWT {
|
|
|
58
57
|
catch {
|
|
59
58
|
throw new JWTInvalid('Invalid Unsecured JWT');
|
|
60
59
|
}
|
|
61
|
-
const payload =
|
|
60
|
+
const payload = validateClaimsSet(header, b64u.decode(encodedPayload), options);
|
|
62
61
|
return { payload, header };
|
|
63
62
|
}
|
|
64
63
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { compactVerify } from '../jws/compact/verify.js';
|
|
2
|
-
import
|
|
2
|
+
import { validateClaimsSet } from '../lib/jwt_claims_set.js';
|
|
3
3
|
import { JWTInvalid } from '../util/errors.js';
|
|
4
4
|
export async function jwtVerify(jwt, key, options) {
|
|
5
5
|
const verified = await compactVerify(jwt, key, options);
|
|
6
6
|
if (verified.protectedHeader.crit?.includes('b64') && verified.protectedHeader.b64 === false) {
|
|
7
7
|
throw new JWTInvalid('JWTs MUST NOT use unencoded payload');
|
|
8
8
|
}
|
|
9
|
-
const payload =
|
|
9
|
+
const payload = validateClaimsSet(verified.protectedHeader, verified.payload, options);
|
|
10
10
|
const result = { payload, protectedHeader: verified.protectedHeader };
|
|
11
11
|
if (typeof key === 'function') {
|
|
12
12
|
return { ...result, key: verified.key };
|
|
@@ -3,7 +3,19 @@ import { decoder } from './buffer_utils.js';
|
|
|
3
3
|
import epoch from './epoch.js';
|
|
4
4
|
import secs from './secs.js';
|
|
5
5
|
import isObject from './is_object.js';
|
|
6
|
-
|
|
6
|
+
import { encoder } from './buffer_utils.js';
|
|
7
|
+
function validateInput(label, input) {
|
|
8
|
+
if (!Number.isFinite(input)) {
|
|
9
|
+
throw new TypeError(`Invalid ${label} input`);
|
|
10
|
+
}
|
|
11
|
+
return input;
|
|
12
|
+
}
|
|
13
|
+
const normalizeTyp = (value) => {
|
|
14
|
+
if (value.includes('/')) {
|
|
15
|
+
return value.toLowerCase();
|
|
16
|
+
}
|
|
17
|
+
return `application/${value.toLowerCase()}`;
|
|
18
|
+
};
|
|
7
19
|
const checkAudiencePresence = (audPayload, audOption) => {
|
|
8
20
|
if (typeof audPayload === 'string') {
|
|
9
21
|
return audOption.includes(audPayload);
|
|
@@ -13,7 +25,7 @@ const checkAudiencePresence = (audPayload, audOption) => {
|
|
|
13
25
|
}
|
|
14
26
|
return false;
|
|
15
27
|
};
|
|
16
|
-
export
|
|
28
|
+
export function validateClaimsSet(protectedHeader, encodedPayload, options = {}) {
|
|
17
29
|
let payload;
|
|
18
30
|
try {
|
|
19
31
|
payload = JSON.parse(decoder.decode(encodedPayload));
|
|
@@ -101,4 +113,73 @@ export default (protectedHeader, encodedPayload, options = {}) => {
|
|
|
101
113
|
}
|
|
102
114
|
}
|
|
103
115
|
return payload;
|
|
104
|
-
}
|
|
116
|
+
}
|
|
117
|
+
export class JWTClaimsBuilder {
|
|
118
|
+
#payload;
|
|
119
|
+
constructor(payload) {
|
|
120
|
+
if (!isObject(payload)) {
|
|
121
|
+
throw new TypeError('JWT Claims Set MUST be an object');
|
|
122
|
+
}
|
|
123
|
+
this.#payload = structuredClone(payload);
|
|
124
|
+
}
|
|
125
|
+
data() {
|
|
126
|
+
return encoder.encode(JSON.stringify(this.#payload));
|
|
127
|
+
}
|
|
128
|
+
get iss() {
|
|
129
|
+
return this.#payload.iss;
|
|
130
|
+
}
|
|
131
|
+
set iss(value) {
|
|
132
|
+
this.#payload.iss = value;
|
|
133
|
+
}
|
|
134
|
+
get sub() {
|
|
135
|
+
return this.#payload.sub;
|
|
136
|
+
}
|
|
137
|
+
set sub(value) {
|
|
138
|
+
this.#payload.sub = value;
|
|
139
|
+
}
|
|
140
|
+
get aud() {
|
|
141
|
+
return this.#payload.aud;
|
|
142
|
+
}
|
|
143
|
+
set aud(value) {
|
|
144
|
+
this.#payload.aud = value;
|
|
145
|
+
}
|
|
146
|
+
set jti(value) {
|
|
147
|
+
this.#payload.jti = value;
|
|
148
|
+
}
|
|
149
|
+
set nbf(value) {
|
|
150
|
+
if (typeof value === 'number') {
|
|
151
|
+
this.#payload.nbf = validateInput('setNotBefore', value);
|
|
152
|
+
}
|
|
153
|
+
else if (value instanceof Date) {
|
|
154
|
+
this.#payload.nbf = validateInput('setNotBefore', epoch(value));
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
this.#payload.nbf = epoch(new Date()) + secs(value);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
set exp(value) {
|
|
161
|
+
if (typeof value === 'number') {
|
|
162
|
+
this.#payload.exp = validateInput('setExpirationTime', value);
|
|
163
|
+
}
|
|
164
|
+
else if (value instanceof Date) {
|
|
165
|
+
this.#payload.exp = validateInput('setExpirationTime', epoch(value));
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
this.#payload.exp = epoch(new Date()) + secs(value);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
set iat(value) {
|
|
172
|
+
if (typeof value === 'undefined') {
|
|
173
|
+
this.#payload.iat = epoch(new Date());
|
|
174
|
+
}
|
|
175
|
+
else if (value instanceof Date) {
|
|
176
|
+
this.#payload.iat = validateInput('setIssuedAt', epoch(value));
|
|
177
|
+
}
|
|
178
|
+
else if (typeof value === 'string') {
|
|
179
|
+
this.#payload.iat = validateInput('setIssuedAt', epoch(new Date()) + secs(value));
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
this.#payload.iat = validateInput('setIssuedAt', value);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
package/package.json
CHANGED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type * as types from '../types.d.ts';
|
|
2
|
-
export declare class ProduceJWT {
|
|
3
|
-
#private;
|
|
4
|
-
constructor(payload: types.JWTPayload);
|
|
5
|
-
data(): Uint8Array;
|
|
6
|
-
get iss(): string | undefined;
|
|
7
|
-
set iss(value: string);
|
|
8
|
-
get sub(): string | undefined;
|
|
9
|
-
set sub(value: string);
|
|
10
|
-
get aud(): string | string[] | undefined;
|
|
11
|
-
set aud(value: string | string[]);
|
|
12
|
-
get jti(): string | undefined;
|
|
13
|
-
set jti(value: string);
|
|
14
|
-
get nbf(): number | undefined;
|
|
15
|
-
set nbf(value: number | string | Date);
|
|
16
|
-
get exp(): number | undefined;
|
|
17
|
-
set exp(value: number | string | Date);
|
|
18
|
-
get iat(): number | undefined;
|
|
19
|
-
set iat(value: number | string | Date | undefined);
|
|
20
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import epoch from '../lib/epoch.js';
|
|
2
|
-
import secs from '../lib/secs.js';
|
|
3
|
-
import isObject from '../lib/is_object.js';
|
|
4
|
-
import { encoder } from '../lib/buffer_utils.js';
|
|
5
|
-
function validateInput(label, input) {
|
|
6
|
-
if (!Number.isFinite(input)) {
|
|
7
|
-
throw new TypeError(`Invalid ${label} input`);
|
|
8
|
-
}
|
|
9
|
-
return input;
|
|
10
|
-
}
|
|
11
|
-
export class ProduceJWT {
|
|
12
|
-
#payload;
|
|
13
|
-
constructor(payload) {
|
|
14
|
-
if (!isObject(payload)) {
|
|
15
|
-
throw new TypeError('JWT Claims Set MUST be an object');
|
|
16
|
-
}
|
|
17
|
-
this.#payload = structuredClone(payload);
|
|
18
|
-
}
|
|
19
|
-
data() {
|
|
20
|
-
return encoder.encode(JSON.stringify(this.#payload));
|
|
21
|
-
}
|
|
22
|
-
get iss() {
|
|
23
|
-
return this.#payload.iss;
|
|
24
|
-
}
|
|
25
|
-
set iss(value) {
|
|
26
|
-
this.#payload.iss = value;
|
|
27
|
-
}
|
|
28
|
-
get sub() {
|
|
29
|
-
return this.#payload.sub;
|
|
30
|
-
}
|
|
31
|
-
set sub(value) {
|
|
32
|
-
this.#payload.sub = value;
|
|
33
|
-
}
|
|
34
|
-
get aud() {
|
|
35
|
-
return this.#payload.aud;
|
|
36
|
-
}
|
|
37
|
-
set aud(value) {
|
|
38
|
-
this.#payload.aud = value;
|
|
39
|
-
}
|
|
40
|
-
get jti() {
|
|
41
|
-
return this.#payload.jti;
|
|
42
|
-
}
|
|
43
|
-
set jti(value) {
|
|
44
|
-
this.#payload.jti = value;
|
|
45
|
-
}
|
|
46
|
-
get nbf() {
|
|
47
|
-
return this.#payload.nbf;
|
|
48
|
-
}
|
|
49
|
-
set nbf(value) {
|
|
50
|
-
if (typeof value === 'number') {
|
|
51
|
-
this.#payload.nbf = validateInput('setNotBefore', value);
|
|
52
|
-
}
|
|
53
|
-
else if (value instanceof Date) {
|
|
54
|
-
this.#payload.nbf = validateInput('setNotBefore', epoch(value));
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
this.#payload.nbf = epoch(new Date()) + secs(value);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
get exp() {
|
|
61
|
-
return this.#payload.exp;
|
|
62
|
-
}
|
|
63
|
-
set exp(value) {
|
|
64
|
-
if (typeof value === 'number') {
|
|
65
|
-
this.#payload.exp = validateInput('setExpirationTime', value);
|
|
66
|
-
}
|
|
67
|
-
else if (value instanceof Date) {
|
|
68
|
-
this.#payload.exp = validateInput('setExpirationTime', epoch(value));
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
this.#payload.exp = epoch(new Date()) + secs(value);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
get iat() {
|
|
75
|
-
return this.#payload.iat;
|
|
76
|
-
}
|
|
77
|
-
set iat(value) {
|
|
78
|
-
if (typeof value === 'undefined') {
|
|
79
|
-
this.#payload.iat = epoch(new Date());
|
|
80
|
-
}
|
|
81
|
-
else if (value instanceof Date) {
|
|
82
|
-
this.#payload.iat = validateInput('setIssuedAt', epoch(value));
|
|
83
|
-
}
|
|
84
|
-
else if (typeof value === 'string') {
|
|
85
|
-
this.#payload.iat = validateInput('setIssuedAt', epoch(new Date()) + secs(value));
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
this.#payload.iat = validateInput('setIssuedAt', value);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|