jose 5.2.4 → 5.4.0
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/dist/browser/jwe/flattened/encrypt.js +4 -6
- package/dist/browser/jwks/local.js +10 -1
- package/dist/browser/jwks/remote.js +32 -2
- package/dist/browser/jwt/decrypt.js +3 -3
- package/dist/browser/lib/jwt_claims_set.js +17 -17
- package/dist/browser/util/errors.js +4 -2
- package/dist/node/cjs/jwe/flattened/encrypt.js +4 -6
- package/dist/node/cjs/jwks/local.js +10 -1
- package/dist/node/cjs/jwks/remote.js +32 -2
- package/dist/node/cjs/jwt/decrypt.js +3 -3
- package/dist/node/cjs/lib/jwt_claims_set.js +17 -17
- package/dist/node/cjs/util/errors.js +6 -2
- package/dist/node/esm/jwe/flattened/encrypt.js +4 -6
- package/dist/node/esm/jwks/local.js +10 -1
- package/dist/node/esm/jwks/remote.js +32 -2
- package/dist/node/esm/jwt/decrypt.js +3 -3
- package/dist/node/esm/lib/jwt_claims_set.js +17 -17
- package/dist/node/esm/util/errors.js +6 -2
- package/dist/types/jwks/local.d.ts +1 -1
- package/dist/types/jwks/remote.d.ts +14 -2
- package/dist/types/util/errors.d.ts +7 -3
- package/package.json +1 -1
|
@@ -99,13 +99,11 @@ export class FlattenedEncrypt {
|
|
|
99
99
|
this._unprotectedHeader = { ...this._unprotectedHeader, ...parameters };
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
|
+
else if (!this._protectedHeader) {
|
|
103
|
+
this.setProtectedHeader(parameters);
|
|
104
|
+
}
|
|
102
105
|
else {
|
|
103
|
-
|
|
104
|
-
this.setProtectedHeader(parameters);
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
this._protectedHeader = { ...this._protectedHeader, ...parameters };
|
|
108
|
-
}
|
|
106
|
+
this._protectedHeader = { ...this._protectedHeader, ...parameters };
|
|
109
107
|
}
|
|
110
108
|
}
|
|
111
109
|
}
|
|
@@ -108,5 +108,14 @@ async function importWithAlgCache(cache, jwk, alg) {
|
|
|
108
108
|
}
|
|
109
109
|
export function createLocalJWKSet(jwks) {
|
|
110
110
|
const set = new LocalJWKSet(jwks);
|
|
111
|
-
|
|
111
|
+
const localJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
|
|
112
|
+
Object.defineProperties(localJWKSet, {
|
|
113
|
+
jwks: {
|
|
114
|
+
value: () => clone(set._jwks),
|
|
115
|
+
enumerable: true,
|
|
116
|
+
configurable: false,
|
|
117
|
+
writable: false,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
return localJWKSet;
|
|
112
121
|
}
|
|
@@ -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 = 'v5.
|
|
12
|
+
const VERSION = 'v5.4.0';
|
|
13
13
|
USER_AGENT = `${NAME}/${VERSION}`;
|
|
14
14
|
}
|
|
15
15
|
class RemoteJWKSet {
|
|
@@ -76,5 +76,35 @@ class RemoteJWKSet {
|
|
|
76
76
|
}
|
|
77
77
|
export function createRemoteJWKSet(url, options) {
|
|
78
78
|
const set = new RemoteJWKSet(url, options);
|
|
79
|
-
|
|
79
|
+
const remoteJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
|
|
80
|
+
Object.defineProperties(remoteJWKSet, {
|
|
81
|
+
coolingDown: {
|
|
82
|
+
get: () => set.coolingDown(),
|
|
83
|
+
enumerable: true,
|
|
84
|
+
configurable: false,
|
|
85
|
+
},
|
|
86
|
+
fresh: {
|
|
87
|
+
get: () => set.fresh(),
|
|
88
|
+
enumerable: true,
|
|
89
|
+
configurable: false,
|
|
90
|
+
},
|
|
91
|
+
reload: {
|
|
92
|
+
value: () => set.reload(),
|
|
93
|
+
enumerable: true,
|
|
94
|
+
configurable: false,
|
|
95
|
+
writable: false,
|
|
96
|
+
},
|
|
97
|
+
reloading: {
|
|
98
|
+
get: () => !!set._pendingFetch,
|
|
99
|
+
enumerable: true,
|
|
100
|
+
configurable: false,
|
|
101
|
+
},
|
|
102
|
+
jwks: {
|
|
103
|
+
value: () => set._local?.jwks(),
|
|
104
|
+
enumerable: true,
|
|
105
|
+
configurable: false,
|
|
106
|
+
writable: false,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
return remoteJWKSet;
|
|
80
110
|
}
|
|
@@ -6,14 +6,14 @@ export async function jwtDecrypt(jwt, key, options) {
|
|
|
6
6
|
const payload = jwtPayload(decrypted.protectedHeader, decrypted.plaintext, options);
|
|
7
7
|
const { protectedHeader } = decrypted;
|
|
8
8
|
if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) {
|
|
9
|
-
throw new JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', 'iss', 'mismatch');
|
|
9
|
+
throw new JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', payload, 'iss', 'mismatch');
|
|
10
10
|
}
|
|
11
11
|
if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) {
|
|
12
|
-
throw new JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', 'sub', 'mismatch');
|
|
12
|
+
throw new JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', payload, 'sub', 'mismatch');
|
|
13
13
|
}
|
|
14
14
|
if (protectedHeader.aud !== undefined &&
|
|
15
15
|
JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud)) {
|
|
16
|
-
throw new JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', 'aud', 'mismatch');
|
|
16
|
+
throw new JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', payload, 'aud', 'mismatch');
|
|
17
17
|
}
|
|
18
18
|
const result = { payload, protectedHeader };
|
|
19
19
|
if (typeof key === 'function') {
|
|
@@ -14,12 +14,6 @@ const checkAudiencePresence = (audPayload, audOption) => {
|
|
|
14
14
|
return false;
|
|
15
15
|
};
|
|
16
16
|
export default (protectedHeader, encodedPayload, options = {}) => {
|
|
17
|
-
const { typ } = options;
|
|
18
|
-
if (typ &&
|
|
19
|
-
(typeof protectedHeader.typ !== 'string' ||
|
|
20
|
-
normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
|
|
21
|
-
throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', 'typ', 'check_failed');
|
|
22
|
-
}
|
|
23
17
|
let payload;
|
|
24
18
|
try {
|
|
25
19
|
payload = JSON.parse(decoder.decode(encodedPayload));
|
|
@@ -29,6 +23,12 @@ export default (protectedHeader, encodedPayload, options = {}) => {
|
|
|
29
23
|
if (!isObject(payload)) {
|
|
30
24
|
throw new JWTInvalid('JWT Claims Set must be a top-level JSON object');
|
|
31
25
|
}
|
|
26
|
+
const { typ } = options;
|
|
27
|
+
if (typ &&
|
|
28
|
+
(typeof protectedHeader.typ !== 'string' ||
|
|
29
|
+
normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
|
|
30
|
+
throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, 'typ', 'check_failed');
|
|
31
|
+
}
|
|
32
32
|
const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options;
|
|
33
33
|
const presenceCheck = [...requiredClaims];
|
|
34
34
|
if (maxTokenAge !== undefined)
|
|
@@ -41,18 +41,18 @@ export default (protectedHeader, encodedPayload, options = {}) => {
|
|
|
41
41
|
presenceCheck.push('iss');
|
|
42
42
|
for (const claim of new Set(presenceCheck.reverse())) {
|
|
43
43
|
if (!(claim in payload)) {
|
|
44
|
-
throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, claim, 'missing');
|
|
44
|
+
throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, payload, claim, 'missing');
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
if (issuer && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) {
|
|
48
|
-
throw new JWTClaimValidationFailed('unexpected "iss" claim value', 'iss', 'check_failed');
|
|
48
|
+
throw new JWTClaimValidationFailed('unexpected "iss" claim value', payload, 'iss', 'check_failed');
|
|
49
49
|
}
|
|
50
50
|
if (subject && payload.sub !== subject) {
|
|
51
|
-
throw new JWTClaimValidationFailed('unexpected "sub" claim value', 'sub', 'check_failed');
|
|
51
|
+
throw new JWTClaimValidationFailed('unexpected "sub" claim value', payload, 'sub', 'check_failed');
|
|
52
52
|
}
|
|
53
53
|
if (audience &&
|
|
54
54
|
!checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) {
|
|
55
|
-
throw new JWTClaimValidationFailed('unexpected "aud" claim value', 'aud', 'check_failed');
|
|
55
|
+
throw new JWTClaimValidationFailed('unexpected "aud" claim value', payload, 'aud', 'check_failed');
|
|
56
56
|
}
|
|
57
57
|
let tolerance;
|
|
58
58
|
switch (typeof options.clockTolerance) {
|
|
@@ -71,32 +71,32 @@ export default (protectedHeader, encodedPayload, options = {}) => {
|
|
|
71
71
|
const { currentDate } = options;
|
|
72
72
|
const now = epoch(currentDate || new Date());
|
|
73
73
|
if ((payload.iat !== undefined || maxTokenAge) && typeof payload.iat !== 'number') {
|
|
74
|
-
throw new JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid');
|
|
74
|
+
throw new JWTClaimValidationFailed('"iat" claim must be a number', payload, 'iat', 'invalid');
|
|
75
75
|
}
|
|
76
76
|
if (payload.nbf !== undefined) {
|
|
77
77
|
if (typeof payload.nbf !== 'number') {
|
|
78
|
-
throw new JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid');
|
|
78
|
+
throw new JWTClaimValidationFailed('"nbf" claim must be a number', payload, 'nbf', 'invalid');
|
|
79
79
|
}
|
|
80
80
|
if (payload.nbf > now + tolerance) {
|
|
81
|
-
throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', 'nbf', 'check_failed');
|
|
81
|
+
throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', payload, 'nbf', 'check_failed');
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
if (payload.exp !== undefined) {
|
|
85
85
|
if (typeof payload.exp !== 'number') {
|
|
86
|
-
throw new JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid');
|
|
86
|
+
throw new JWTClaimValidationFailed('"exp" claim must be a number', payload, 'exp', 'invalid');
|
|
87
87
|
}
|
|
88
88
|
if (payload.exp <= now - tolerance) {
|
|
89
|
-
throw new JWTExpired('"exp" claim timestamp check failed', 'exp', 'check_failed');
|
|
89
|
+
throw new JWTExpired('"exp" claim timestamp check failed', payload, 'exp', 'check_failed');
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
if (maxTokenAge) {
|
|
93
93
|
const age = now - payload.iat;
|
|
94
94
|
const max = typeof maxTokenAge === 'number' ? maxTokenAge : secs(maxTokenAge);
|
|
95
95
|
if (age - tolerance > max) {
|
|
96
|
-
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', 'iat', 'check_failed');
|
|
96
|
+
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, 'iat', 'check_failed');
|
|
97
97
|
}
|
|
98
98
|
if (age < 0 - tolerance) {
|
|
99
|
-
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed');
|
|
99
|
+
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, 'iat', 'check_failed');
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
return payload;
|
|
@@ -13,22 +13,24 @@ export class JWTClaimValidationFailed extends JOSEError {
|
|
|
13
13
|
static get code() {
|
|
14
14
|
return 'ERR_JWT_CLAIM_VALIDATION_FAILED';
|
|
15
15
|
}
|
|
16
|
-
constructor(message, claim = 'unspecified', reason = 'unspecified') {
|
|
16
|
+
constructor(message, payload, claim = 'unspecified', reason = 'unspecified') {
|
|
17
17
|
super(message);
|
|
18
18
|
this.code = 'ERR_JWT_CLAIM_VALIDATION_FAILED';
|
|
19
19
|
this.claim = claim;
|
|
20
20
|
this.reason = reason;
|
|
21
|
+
this.payload = payload;
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
export class JWTExpired extends JOSEError {
|
|
24
25
|
static get code() {
|
|
25
26
|
return 'ERR_JWT_EXPIRED';
|
|
26
27
|
}
|
|
27
|
-
constructor(message, claim = 'unspecified', reason = 'unspecified') {
|
|
28
|
+
constructor(message, payload, claim = 'unspecified', reason = 'unspecified') {
|
|
28
29
|
super(message);
|
|
29
30
|
this.code = 'ERR_JWT_EXPIRED';
|
|
30
31
|
this.claim = claim;
|
|
31
32
|
this.reason = reason;
|
|
33
|
+
this.payload = payload;
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
export class JOSEAlgNotAllowed extends JOSEError {
|
|
@@ -110,13 +110,11 @@ class FlattenedEncrypt {
|
|
|
110
110
|
this._unprotectedHeader = { ...this._unprotectedHeader, ...parameters };
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
|
+
else if (!this._protectedHeader) {
|
|
114
|
+
this.setProtectedHeader(parameters);
|
|
115
|
+
}
|
|
113
116
|
else {
|
|
114
|
-
|
|
115
|
-
this.setProtectedHeader(parameters);
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
this._protectedHeader = { ...this._protectedHeader, ...parameters };
|
|
119
|
-
}
|
|
117
|
+
this._protectedHeader = { ...this._protectedHeader, ...parameters };
|
|
120
118
|
}
|
|
121
119
|
}
|
|
122
120
|
}
|
|
@@ -113,6 +113,15 @@ async function importWithAlgCache(cache, jwk, alg) {
|
|
|
113
113
|
}
|
|
114
114
|
function createLocalJWKSet(jwks) {
|
|
115
115
|
const set = new LocalJWKSet(jwks);
|
|
116
|
-
|
|
116
|
+
const localJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
|
|
117
|
+
Object.defineProperties(localJWKSet, {
|
|
118
|
+
jwks: {
|
|
119
|
+
value: () => clone(set._jwks),
|
|
120
|
+
enumerable: true,
|
|
121
|
+
configurable: false,
|
|
122
|
+
writable: false,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
return localJWKSet;
|
|
117
126
|
}
|
|
118
127
|
exports.createLocalJWKSet = createLocalJWKSet;
|
|
@@ -12,7 +12,7 @@ function isCloudflareWorkers() {
|
|
|
12
12
|
let USER_AGENT;
|
|
13
13
|
if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
|
|
14
14
|
const NAME = 'jose';
|
|
15
|
-
const VERSION = 'v5.
|
|
15
|
+
const VERSION = 'v5.4.0';
|
|
16
16
|
USER_AGENT = `${NAME}/${VERSION}`;
|
|
17
17
|
}
|
|
18
18
|
class RemoteJWKSet {
|
|
@@ -87,6 +87,36 @@ class RemoteJWKSet {
|
|
|
87
87
|
}
|
|
88
88
|
function createRemoteJWKSet(url, options) {
|
|
89
89
|
const set = new RemoteJWKSet(url, options);
|
|
90
|
-
|
|
90
|
+
const remoteJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
|
|
91
|
+
Object.defineProperties(remoteJWKSet, {
|
|
92
|
+
coolingDown: {
|
|
93
|
+
get: () => set.coolingDown(),
|
|
94
|
+
enumerable: true,
|
|
95
|
+
configurable: false,
|
|
96
|
+
},
|
|
97
|
+
fresh: {
|
|
98
|
+
get: () => set.fresh(),
|
|
99
|
+
enumerable: true,
|
|
100
|
+
configurable: false,
|
|
101
|
+
},
|
|
102
|
+
reload: {
|
|
103
|
+
value: () => set.reload(),
|
|
104
|
+
enumerable: true,
|
|
105
|
+
configurable: false,
|
|
106
|
+
writable: false,
|
|
107
|
+
},
|
|
108
|
+
reloading: {
|
|
109
|
+
get: () => !!set._pendingFetch,
|
|
110
|
+
enumerable: true,
|
|
111
|
+
configurable: false,
|
|
112
|
+
},
|
|
113
|
+
jwks: {
|
|
114
|
+
value: () => set._local?.jwks(),
|
|
115
|
+
enumerable: true,
|
|
116
|
+
configurable: false,
|
|
117
|
+
writable: false,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
return remoteJWKSet;
|
|
91
121
|
}
|
|
92
122
|
exports.createRemoteJWKSet = createRemoteJWKSet;
|
|
@@ -9,14 +9,14 @@ async function jwtDecrypt(jwt, key, options) {
|
|
|
9
9
|
const payload = (0, jwt_claims_set_js_1.default)(decrypted.protectedHeader, decrypted.plaintext, options);
|
|
10
10
|
const { protectedHeader } = decrypted;
|
|
11
11
|
if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) {
|
|
12
|
-
throw new errors_js_1.JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', 'iss', 'mismatch');
|
|
12
|
+
throw new errors_js_1.JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', payload, 'iss', 'mismatch');
|
|
13
13
|
}
|
|
14
14
|
if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) {
|
|
15
|
-
throw new errors_js_1.JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', 'sub', 'mismatch');
|
|
15
|
+
throw new errors_js_1.JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', payload, 'sub', 'mismatch');
|
|
16
16
|
}
|
|
17
17
|
if (protectedHeader.aud !== undefined &&
|
|
18
18
|
JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud)) {
|
|
19
|
-
throw new errors_js_1.JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', 'aud', 'mismatch');
|
|
19
|
+
throw new errors_js_1.JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', payload, 'aud', 'mismatch');
|
|
20
20
|
}
|
|
21
21
|
const result = { payload, protectedHeader };
|
|
22
22
|
if (typeof key === 'function') {
|
|
@@ -16,12 +16,6 @@ const checkAudiencePresence = (audPayload, audOption) => {
|
|
|
16
16
|
return false;
|
|
17
17
|
};
|
|
18
18
|
exports.default = (protectedHeader, encodedPayload, options = {}) => {
|
|
19
|
-
const { typ } = options;
|
|
20
|
-
if (typ &&
|
|
21
|
-
(typeof protectedHeader.typ !== 'string' ||
|
|
22
|
-
normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
|
|
23
|
-
throw new errors_js_1.JWTClaimValidationFailed('unexpected "typ" JWT header value', 'typ', 'check_failed');
|
|
24
|
-
}
|
|
25
19
|
let payload;
|
|
26
20
|
try {
|
|
27
21
|
payload = JSON.parse(buffer_utils_js_1.decoder.decode(encodedPayload));
|
|
@@ -31,6 +25,12 @@ exports.default = (protectedHeader, encodedPayload, options = {}) => {
|
|
|
31
25
|
if (!(0, is_object_js_1.default)(payload)) {
|
|
32
26
|
throw new errors_js_1.JWTInvalid('JWT Claims Set must be a top-level JSON object');
|
|
33
27
|
}
|
|
28
|
+
const { typ } = options;
|
|
29
|
+
if (typ &&
|
|
30
|
+
(typeof protectedHeader.typ !== 'string' ||
|
|
31
|
+
normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
|
|
32
|
+
throw new errors_js_1.JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, 'typ', 'check_failed');
|
|
33
|
+
}
|
|
34
34
|
const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options;
|
|
35
35
|
const presenceCheck = [...requiredClaims];
|
|
36
36
|
if (maxTokenAge !== undefined)
|
|
@@ -43,18 +43,18 @@ exports.default = (protectedHeader, encodedPayload, options = {}) => {
|
|
|
43
43
|
presenceCheck.push('iss');
|
|
44
44
|
for (const claim of new Set(presenceCheck.reverse())) {
|
|
45
45
|
if (!(claim in payload)) {
|
|
46
|
-
throw new errors_js_1.JWTClaimValidationFailed(`missing required "${claim}" claim`, claim, 'missing');
|
|
46
|
+
throw new errors_js_1.JWTClaimValidationFailed(`missing required "${claim}" claim`, payload, claim, 'missing');
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
if (issuer && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) {
|
|
50
|
-
throw new errors_js_1.JWTClaimValidationFailed('unexpected "iss" claim value', 'iss', 'check_failed');
|
|
50
|
+
throw new errors_js_1.JWTClaimValidationFailed('unexpected "iss" claim value', payload, 'iss', 'check_failed');
|
|
51
51
|
}
|
|
52
52
|
if (subject && payload.sub !== subject) {
|
|
53
|
-
throw new errors_js_1.JWTClaimValidationFailed('unexpected "sub" claim value', 'sub', 'check_failed');
|
|
53
|
+
throw new errors_js_1.JWTClaimValidationFailed('unexpected "sub" claim value', payload, 'sub', 'check_failed');
|
|
54
54
|
}
|
|
55
55
|
if (audience &&
|
|
56
56
|
!checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) {
|
|
57
|
-
throw new errors_js_1.JWTClaimValidationFailed('unexpected "aud" claim value', 'aud', 'check_failed');
|
|
57
|
+
throw new errors_js_1.JWTClaimValidationFailed('unexpected "aud" claim value', payload, 'aud', 'check_failed');
|
|
58
58
|
}
|
|
59
59
|
let tolerance;
|
|
60
60
|
switch (typeof options.clockTolerance) {
|
|
@@ -73,32 +73,32 @@ exports.default = (protectedHeader, encodedPayload, options = {}) => {
|
|
|
73
73
|
const { currentDate } = options;
|
|
74
74
|
const now = (0, epoch_js_1.default)(currentDate || new Date());
|
|
75
75
|
if ((payload.iat !== undefined || maxTokenAge) && typeof payload.iat !== 'number') {
|
|
76
|
-
throw new errors_js_1.JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid');
|
|
76
|
+
throw new errors_js_1.JWTClaimValidationFailed('"iat" claim must be a number', payload, 'iat', 'invalid');
|
|
77
77
|
}
|
|
78
78
|
if (payload.nbf !== undefined) {
|
|
79
79
|
if (typeof payload.nbf !== 'number') {
|
|
80
|
-
throw new errors_js_1.JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid');
|
|
80
|
+
throw new errors_js_1.JWTClaimValidationFailed('"nbf" claim must be a number', payload, 'nbf', 'invalid');
|
|
81
81
|
}
|
|
82
82
|
if (payload.nbf > now + tolerance) {
|
|
83
|
-
throw new errors_js_1.JWTClaimValidationFailed('"nbf" claim timestamp check failed', 'nbf', 'check_failed');
|
|
83
|
+
throw new errors_js_1.JWTClaimValidationFailed('"nbf" claim timestamp check failed', payload, 'nbf', 'check_failed');
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
if (payload.exp !== undefined) {
|
|
87
87
|
if (typeof payload.exp !== 'number') {
|
|
88
|
-
throw new errors_js_1.JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid');
|
|
88
|
+
throw new errors_js_1.JWTClaimValidationFailed('"exp" claim must be a number', payload, 'exp', 'invalid');
|
|
89
89
|
}
|
|
90
90
|
if (payload.exp <= now - tolerance) {
|
|
91
|
-
throw new errors_js_1.JWTExpired('"exp" claim timestamp check failed', 'exp', 'check_failed');
|
|
91
|
+
throw new errors_js_1.JWTExpired('"exp" claim timestamp check failed', payload, 'exp', 'check_failed');
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
if (maxTokenAge) {
|
|
95
95
|
const age = now - payload.iat;
|
|
96
96
|
const max = typeof maxTokenAge === 'number' ? maxTokenAge : (0, secs_js_1.default)(maxTokenAge);
|
|
97
97
|
if (age - tolerance > max) {
|
|
98
|
-
throw new errors_js_1.JWTExpired('"iat" claim timestamp check failed (too far in the past)', 'iat', 'check_failed');
|
|
98
|
+
throw new errors_js_1.JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, 'iat', 'check_failed');
|
|
99
99
|
}
|
|
100
100
|
if (age < 0 - tolerance) {
|
|
101
|
-
throw new errors_js_1.JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed');
|
|
101
|
+
throw new errors_js_1.JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, 'iat', 'check_failed');
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
return payload;
|
|
@@ -20,10 +20,12 @@ class JWTClaimValidationFailed extends JOSEError {
|
|
|
20
20
|
code = 'ERR_JWT_CLAIM_VALIDATION_FAILED';
|
|
21
21
|
claim;
|
|
22
22
|
reason;
|
|
23
|
-
|
|
23
|
+
payload;
|
|
24
|
+
constructor(message, payload, claim = 'unspecified', reason = 'unspecified') {
|
|
24
25
|
super(message);
|
|
25
26
|
this.claim = claim;
|
|
26
27
|
this.reason = reason;
|
|
28
|
+
this.payload = payload;
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
exports.JWTClaimValidationFailed = JWTClaimValidationFailed;
|
|
@@ -34,10 +36,12 @@ class JWTExpired extends JOSEError {
|
|
|
34
36
|
code = 'ERR_JWT_EXPIRED';
|
|
35
37
|
claim;
|
|
36
38
|
reason;
|
|
37
|
-
|
|
39
|
+
payload;
|
|
40
|
+
constructor(message, payload, claim = 'unspecified', reason = 'unspecified') {
|
|
38
41
|
super(message);
|
|
39
42
|
this.claim = claim;
|
|
40
43
|
this.reason = reason;
|
|
44
|
+
this.payload = payload;
|
|
41
45
|
}
|
|
42
46
|
}
|
|
43
47
|
exports.JWTExpired = JWTExpired;
|
|
@@ -107,13 +107,11 @@ export class FlattenedEncrypt {
|
|
|
107
107
|
this._unprotectedHeader = { ...this._unprotectedHeader, ...parameters };
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
+
else if (!this._protectedHeader) {
|
|
111
|
+
this.setProtectedHeader(parameters);
|
|
112
|
+
}
|
|
110
113
|
else {
|
|
111
|
-
|
|
112
|
-
this.setProtectedHeader(parameters);
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
this._protectedHeader = { ...this._protectedHeader, ...parameters };
|
|
116
|
-
}
|
|
114
|
+
this._protectedHeader = { ...this._protectedHeader, ...parameters };
|
|
117
115
|
}
|
|
118
116
|
}
|
|
119
117
|
}
|
|
@@ -109,5 +109,14 @@ async function importWithAlgCache(cache, jwk, alg) {
|
|
|
109
109
|
}
|
|
110
110
|
export function createLocalJWKSet(jwks) {
|
|
111
111
|
const set = new LocalJWKSet(jwks);
|
|
112
|
-
|
|
112
|
+
const localJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
|
|
113
|
+
Object.defineProperties(localJWKSet, {
|
|
114
|
+
jwks: {
|
|
115
|
+
value: () => clone(set._jwks),
|
|
116
|
+
enumerable: true,
|
|
117
|
+
configurable: false,
|
|
118
|
+
writable: false,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
return localJWKSet;
|
|
113
122
|
}
|
|
@@ -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 = 'v5.
|
|
12
|
+
const VERSION = 'v5.4.0';
|
|
13
13
|
USER_AGENT = `${NAME}/${VERSION}`;
|
|
14
14
|
}
|
|
15
15
|
class RemoteJWKSet {
|
|
@@ -84,5 +84,35 @@ class RemoteJWKSet {
|
|
|
84
84
|
}
|
|
85
85
|
export function createRemoteJWKSet(url, options) {
|
|
86
86
|
const set = new RemoteJWKSet(url, options);
|
|
87
|
-
|
|
87
|
+
const remoteJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
|
|
88
|
+
Object.defineProperties(remoteJWKSet, {
|
|
89
|
+
coolingDown: {
|
|
90
|
+
get: () => set.coolingDown(),
|
|
91
|
+
enumerable: true,
|
|
92
|
+
configurable: false,
|
|
93
|
+
},
|
|
94
|
+
fresh: {
|
|
95
|
+
get: () => set.fresh(),
|
|
96
|
+
enumerable: true,
|
|
97
|
+
configurable: false,
|
|
98
|
+
},
|
|
99
|
+
reload: {
|
|
100
|
+
value: () => set.reload(),
|
|
101
|
+
enumerable: true,
|
|
102
|
+
configurable: false,
|
|
103
|
+
writable: false,
|
|
104
|
+
},
|
|
105
|
+
reloading: {
|
|
106
|
+
get: () => !!set._pendingFetch,
|
|
107
|
+
enumerable: true,
|
|
108
|
+
configurable: false,
|
|
109
|
+
},
|
|
110
|
+
jwks: {
|
|
111
|
+
value: () => set._local?.jwks(),
|
|
112
|
+
enumerable: true,
|
|
113
|
+
configurable: false,
|
|
114
|
+
writable: false,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
return remoteJWKSet;
|
|
88
118
|
}
|
|
@@ -6,14 +6,14 @@ export async function jwtDecrypt(jwt, key, options) {
|
|
|
6
6
|
const payload = jwtPayload(decrypted.protectedHeader, decrypted.plaintext, options);
|
|
7
7
|
const { protectedHeader } = decrypted;
|
|
8
8
|
if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) {
|
|
9
|
-
throw new JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', 'iss', 'mismatch');
|
|
9
|
+
throw new JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', payload, 'iss', 'mismatch');
|
|
10
10
|
}
|
|
11
11
|
if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) {
|
|
12
|
-
throw new JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', 'sub', 'mismatch');
|
|
12
|
+
throw new JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', payload, 'sub', 'mismatch');
|
|
13
13
|
}
|
|
14
14
|
if (protectedHeader.aud !== undefined &&
|
|
15
15
|
JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud)) {
|
|
16
|
-
throw new JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', 'aud', 'mismatch');
|
|
16
|
+
throw new JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', payload, 'aud', 'mismatch');
|
|
17
17
|
}
|
|
18
18
|
const result = { payload, protectedHeader };
|
|
19
19
|
if (typeof key === 'function') {
|
|
@@ -14,12 +14,6 @@ const checkAudiencePresence = (audPayload, audOption) => {
|
|
|
14
14
|
return false;
|
|
15
15
|
};
|
|
16
16
|
export default (protectedHeader, encodedPayload, options = {}) => {
|
|
17
|
-
const { typ } = options;
|
|
18
|
-
if (typ &&
|
|
19
|
-
(typeof protectedHeader.typ !== 'string' ||
|
|
20
|
-
normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
|
|
21
|
-
throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', 'typ', 'check_failed');
|
|
22
|
-
}
|
|
23
17
|
let payload;
|
|
24
18
|
try {
|
|
25
19
|
payload = JSON.parse(decoder.decode(encodedPayload));
|
|
@@ -29,6 +23,12 @@ export default (protectedHeader, encodedPayload, options = {}) => {
|
|
|
29
23
|
if (!isObject(payload)) {
|
|
30
24
|
throw new JWTInvalid('JWT Claims Set must be a top-level JSON object');
|
|
31
25
|
}
|
|
26
|
+
const { typ } = options;
|
|
27
|
+
if (typ &&
|
|
28
|
+
(typeof protectedHeader.typ !== 'string' ||
|
|
29
|
+
normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
|
|
30
|
+
throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, 'typ', 'check_failed');
|
|
31
|
+
}
|
|
32
32
|
const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options;
|
|
33
33
|
const presenceCheck = [...requiredClaims];
|
|
34
34
|
if (maxTokenAge !== undefined)
|
|
@@ -41,18 +41,18 @@ export default (protectedHeader, encodedPayload, options = {}) => {
|
|
|
41
41
|
presenceCheck.push('iss');
|
|
42
42
|
for (const claim of new Set(presenceCheck.reverse())) {
|
|
43
43
|
if (!(claim in payload)) {
|
|
44
|
-
throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, claim, 'missing');
|
|
44
|
+
throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, payload, claim, 'missing');
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
if (issuer && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) {
|
|
48
|
-
throw new JWTClaimValidationFailed('unexpected "iss" claim value', 'iss', 'check_failed');
|
|
48
|
+
throw new JWTClaimValidationFailed('unexpected "iss" claim value', payload, 'iss', 'check_failed');
|
|
49
49
|
}
|
|
50
50
|
if (subject && payload.sub !== subject) {
|
|
51
|
-
throw new JWTClaimValidationFailed('unexpected "sub" claim value', 'sub', 'check_failed');
|
|
51
|
+
throw new JWTClaimValidationFailed('unexpected "sub" claim value', payload, 'sub', 'check_failed');
|
|
52
52
|
}
|
|
53
53
|
if (audience &&
|
|
54
54
|
!checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) {
|
|
55
|
-
throw new JWTClaimValidationFailed('unexpected "aud" claim value', 'aud', 'check_failed');
|
|
55
|
+
throw new JWTClaimValidationFailed('unexpected "aud" claim value', payload, 'aud', 'check_failed');
|
|
56
56
|
}
|
|
57
57
|
let tolerance;
|
|
58
58
|
switch (typeof options.clockTolerance) {
|
|
@@ -71,32 +71,32 @@ export default (protectedHeader, encodedPayload, options = {}) => {
|
|
|
71
71
|
const { currentDate } = options;
|
|
72
72
|
const now = epoch(currentDate || new Date());
|
|
73
73
|
if ((payload.iat !== undefined || maxTokenAge) && typeof payload.iat !== 'number') {
|
|
74
|
-
throw new JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid');
|
|
74
|
+
throw new JWTClaimValidationFailed('"iat" claim must be a number', payload, 'iat', 'invalid');
|
|
75
75
|
}
|
|
76
76
|
if (payload.nbf !== undefined) {
|
|
77
77
|
if (typeof payload.nbf !== 'number') {
|
|
78
|
-
throw new JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid');
|
|
78
|
+
throw new JWTClaimValidationFailed('"nbf" claim must be a number', payload, 'nbf', 'invalid');
|
|
79
79
|
}
|
|
80
80
|
if (payload.nbf > now + tolerance) {
|
|
81
|
-
throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', 'nbf', 'check_failed');
|
|
81
|
+
throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', payload, 'nbf', 'check_failed');
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
if (payload.exp !== undefined) {
|
|
85
85
|
if (typeof payload.exp !== 'number') {
|
|
86
|
-
throw new JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid');
|
|
86
|
+
throw new JWTClaimValidationFailed('"exp" claim must be a number', payload, 'exp', 'invalid');
|
|
87
87
|
}
|
|
88
88
|
if (payload.exp <= now - tolerance) {
|
|
89
|
-
throw new JWTExpired('"exp" claim timestamp check failed', 'exp', 'check_failed');
|
|
89
|
+
throw new JWTExpired('"exp" claim timestamp check failed', payload, 'exp', 'check_failed');
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
if (maxTokenAge) {
|
|
93
93
|
const age = now - payload.iat;
|
|
94
94
|
const max = typeof maxTokenAge === 'number' ? maxTokenAge : secs(maxTokenAge);
|
|
95
95
|
if (age - tolerance > max) {
|
|
96
|
-
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', 'iat', 'check_failed');
|
|
96
|
+
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, 'iat', 'check_failed');
|
|
97
97
|
}
|
|
98
98
|
if (age < 0 - tolerance) {
|
|
99
|
-
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed');
|
|
99
|
+
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, 'iat', 'check_failed');
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
return payload;
|
|
@@ -16,10 +16,12 @@ export class JWTClaimValidationFailed extends JOSEError {
|
|
|
16
16
|
code = 'ERR_JWT_CLAIM_VALIDATION_FAILED';
|
|
17
17
|
claim;
|
|
18
18
|
reason;
|
|
19
|
-
|
|
19
|
+
payload;
|
|
20
|
+
constructor(message, payload, claim = 'unspecified', reason = 'unspecified') {
|
|
20
21
|
super(message);
|
|
21
22
|
this.claim = claim;
|
|
22
23
|
this.reason = reason;
|
|
24
|
+
this.payload = payload;
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
export class JWTExpired extends JOSEError {
|
|
@@ -29,10 +31,12 @@ export class JWTExpired extends JOSEError {
|
|
|
29
31
|
code = 'ERR_JWT_EXPIRED';
|
|
30
32
|
claim;
|
|
31
33
|
reason;
|
|
32
|
-
|
|
34
|
+
payload;
|
|
35
|
+
constructor(message, payload, claim = 'unspecified', reason = 'unspecified') {
|
|
33
36
|
super(message);
|
|
34
37
|
this.claim = claim;
|
|
35
38
|
this.reason = reason;
|
|
39
|
+
this.payload = payload;
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
42
|
export class JOSEAlgNotAllowed extends JOSEError {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { KeyLike, JWSHeaderParameters, JSONWebKeySet, FlattenedJWSInput } from '../types';
|
|
2
2
|
/** @private */
|
|
3
3
|
export declare class LocalJWKSet<KeyLikeType extends KeyLike = KeyLike> {
|
|
4
|
-
|
|
4
|
+
private _jwks?;
|
|
5
5
|
private _cached;
|
|
6
6
|
constructor(jwks: unknown);
|
|
7
7
|
getKey(protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput): Promise<KeyLikeType>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { KeyLike, JWSHeaderParameters, FlattenedJWSInput } from '../types';
|
|
1
|
+
import type { KeyLike, JWSHeaderParameters, FlattenedJWSInput, JSONWebKeySet } from '../types';
|
|
2
2
|
/** Options for the remote JSON Web Key Set. */
|
|
3
3
|
export interface RemoteJWKSetOptions {
|
|
4
4
|
/**
|
|
@@ -53,4 +53,16 @@ export interface RemoteJWKSetOptions {
|
|
|
53
53
|
* @param url URL to fetch the JSON Web Key Set from.
|
|
54
54
|
* @param options Options for the remote JSON Web Key Set.
|
|
55
55
|
*/
|
|
56
|
-
export declare function createRemoteJWKSet<KeyLikeType extends KeyLike = KeyLike>(url: URL, options?: RemoteJWKSetOptions):
|
|
56
|
+
export declare function createRemoteJWKSet<KeyLikeType extends KeyLike = KeyLike>(url: URL, options?: RemoteJWKSetOptions): {
|
|
57
|
+
(protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput): Promise<KeyLikeType>;
|
|
58
|
+
/** @ignore */
|
|
59
|
+
coolingDown: boolean;
|
|
60
|
+
/** @ignore */
|
|
61
|
+
fresh: boolean;
|
|
62
|
+
/** @ignore */
|
|
63
|
+
reloading: boolean;
|
|
64
|
+
/** @ignore */
|
|
65
|
+
reload: () => Promise<void>;
|
|
66
|
+
/** @ignore */
|
|
67
|
+
jwks: () => JSONWebKeySet | undefined;
|
|
68
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { KeyLike } from '../types';
|
|
1
|
+
import type { JWTPayload, KeyLike } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* A generic Error that all other JOSE specific Error subclasses extend.
|
|
4
4
|
*
|
|
@@ -27,8 +27,10 @@ export declare class JWTClaimValidationFailed extends JOSEError {
|
|
|
27
27
|
claim: string;
|
|
28
28
|
/** Reason code for the validation failure. */
|
|
29
29
|
reason: string;
|
|
30
|
+
/** The parsed JWT payload. */
|
|
31
|
+
payload: JWTPayload;
|
|
30
32
|
/** @ignore */
|
|
31
|
-
constructor(message: string, claim?: string, reason?: string);
|
|
33
|
+
constructor(message: string, payload: JWTPayload, claim?: string, reason?: string);
|
|
32
34
|
}
|
|
33
35
|
/**
|
|
34
36
|
* An error subclass thrown when a JWT is expired.
|
|
@@ -42,8 +44,10 @@ export declare class JWTExpired extends JOSEError implements JWTClaimValidationF
|
|
|
42
44
|
claim: string;
|
|
43
45
|
/** Reason code for the validation failure. */
|
|
44
46
|
reason: string;
|
|
47
|
+
/** The parsed JWT payload. */
|
|
48
|
+
payload: JWTPayload;
|
|
45
49
|
/** @ignore */
|
|
46
|
-
constructor(message: string, claim?: string, reason?: string);
|
|
50
|
+
constructor(message: string, payload: JWTPayload, claim?: string, reason?: string);
|
|
47
51
|
}
|
|
48
52
|
/**
|
|
49
53
|
* An error subclass thrown when a JOSE Algorithm is not allowed per developer preference.
|