@cfworker/jwt 5.0.0 → 5.1.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/parse.js CHANGED
@@ -9,33 +9,45 @@ export async function parseJwt({ jwt, issuer, audience, resolveKey = getKey, ske
9
9
  decoded = decodeJwt(jwt);
10
10
  }
11
11
  catch {
12
- return { valid: false, reason: `Unable to decode JWT.` };
12
+ return {
13
+ valid: false,
14
+ reason: `Unable to decode JWT.`,
15
+ reasonCode: 0
16
+ };
13
17
  }
14
18
  const { typ, alg } = decoded.header;
15
19
  if (typeof typ !== 'undefined' && typ !== 'JWT') {
16
20
  return {
17
21
  valid: false,
18
- reason: `Invalid JWT type ${JSON.stringify(typ)}. Expected "JWT".`
22
+ reason: `Invalid JWT type ${JSON.stringify(typ)}. Expected "JWT".`,
23
+ reasonCode: 0,
24
+ decoded
19
25
  };
20
26
  }
21
27
  if (!algToHash[alg]) {
22
28
  return {
23
29
  valid: false,
24
- reason: `Invalid JWT algorithm ${JSON.stringify(alg)}. Supported: ${algs}.`
30
+ reason: `Invalid JWT algorithm ${JSON.stringify(alg)}. Supported: ${algs}.`,
31
+ reasonCode: 0,
32
+ decoded
25
33
  };
26
34
  }
27
35
  const { sub, aud, iss, iat, exp, nbf } = decoded.payload;
28
36
  if (typeof sub !== 'string') {
29
37
  return {
30
38
  valid: false,
31
- reason: `Subject claim (sub) is required and must be a string. Received ${JSON.stringify(sub)}.`
39
+ reason: `Subject claim (sub) is required and must be a string. Received ${JSON.stringify(sub)}.`,
40
+ reasonCode: 0,
41
+ decoded
32
42
  };
33
43
  }
34
44
  if (typeof aud === 'string') {
35
45
  if (aud !== audience) {
36
46
  return {
37
47
  valid: false,
38
- reason: `Invalid JWT audience claim (aud) ${JSON.stringify(aud)}. Expected "${audience}".`
48
+ reason: `Invalid JWT audience claim (aud) ${JSON.stringify(aud)}. Expected "${audience}".`,
49
+ reasonCode: 0,
50
+ decoded
39
51
  };
40
52
  }
41
53
  }
@@ -45,26 +57,34 @@ export async function parseJwt({ jwt, issuer, audience, resolveKey = getKey, ske
45
57
  if (!aud.includes(audience)) {
46
58
  return {
47
59
  valid: false,
48
- reason: `Invalid JWT audience claim array (aud) ${JSON.stringify(aud)}. Does not include "${audience}".`
60
+ reason: `Invalid JWT audience claim array (aud) ${JSON.stringify(aud)}. Does not include "${audience}".`,
61
+ reasonCode: 0,
62
+ decoded
49
63
  };
50
64
  }
51
65
  }
52
66
  else {
53
67
  return {
54
68
  valid: false,
55
- reason: `Invalid JWT audience claim (aud) ${JSON.stringify(aud)}. Expected a string or a non-empty array of strings.`
69
+ reason: `Invalid JWT audience claim (aud) ${JSON.stringify(aud)}. Expected a string or a non-empty array of strings.`,
70
+ reasonCode: 0,
71
+ decoded
56
72
  };
57
73
  }
58
74
  if (!(iss === issuer || (Array.isArray(issuer) && issuer.includes(iss)))) {
59
75
  return {
60
76
  valid: false,
61
- reason: `Invalid JWT issuer claim (iss) ${JSON.stringify(decoded.payload.iss)}. Expected ${JSON.stringify(issuer)}.`
77
+ reason: `Invalid JWT issuer claim (iss) ${JSON.stringify(decoded.payload.iss)}. Expected ${JSON.stringify(issuer)}.`,
78
+ reasonCode: 0,
79
+ decoded
62
80
  };
63
81
  }
64
82
  if (typeof exp !== 'number') {
65
83
  return {
66
84
  valid: false,
67
- reason: `Invalid JWT expiry date claim (exp) ${JSON.stringify(exp)}. Expected number.`
85
+ reason: `Invalid JWT expiry date claim (exp) ${JSON.stringify(exp)}. Expected number.`,
86
+ reasonCode: 0,
87
+ decoded
68
88
  };
69
89
  }
70
90
  const currentDate = new Date(Date.now());
@@ -74,14 +94,18 @@ export async function parseJwt({ jwt, issuer, audience, resolveKey = getKey, ske
74
94
  if (expired) {
75
95
  return {
76
96
  valid: false,
77
- reason: `JWT is expired. Expiry date: ${expiryDate}; Current date: ${currentDate};`
97
+ reason: `JWT is expired. Expiry date: ${expiryDate}; Current date: ${currentDate};`,
98
+ reasonCode: 1,
99
+ decoded
78
100
  };
79
101
  }
80
102
  if (nbf !== undefined) {
81
103
  if (typeof nbf !== 'number') {
82
104
  return {
83
105
  valid: false,
84
- reason: `Invalid JWT not before date claim (nbf) ${JSON.stringify(nbf)}. Expected number.`
106
+ reason: `Invalid JWT not before date claim (nbf) ${JSON.stringify(nbf)}. Expected number.`,
107
+ reasonCode: 0,
108
+ decoded
85
109
  };
86
110
  }
87
111
  const notBeforeDate = new Date(0);
@@ -90,7 +114,9 @@ export async function parseJwt({ jwt, issuer, audience, resolveKey = getKey, ske
90
114
  if (early) {
91
115
  return {
92
116
  valid: false,
93
- reason: `JWT cannot be used prior to not before date claim (nbf). Not before date: ${notBeforeDate}; Current date: ${currentDate};`
117
+ reason: `JWT cannot be used prior to not before date claim (nbf). Not before date: ${notBeforeDate}; Current date: ${currentDate};`,
118
+ reasonCode: 0,
119
+ decoded
94
120
  };
95
121
  }
96
122
  }
@@ -98,7 +124,9 @@ export async function parseJwt({ jwt, issuer, audience, resolveKey = getKey, ske
98
124
  if (typeof iat !== 'number') {
99
125
  return {
100
126
  valid: false,
101
- reason: `Invalid JWT issued at date claim (iat) ${JSON.stringify(iat)}. Expected number.`
127
+ reason: `Invalid JWT issued at date claim (iat) ${JSON.stringify(iat)}. Expected number.`,
128
+ reasonCode: 0,
129
+ decoded
102
130
  };
103
131
  }
104
132
  const issuedAtDate = new Date(0);
@@ -107,7 +135,9 @@ export async function parseJwt({ jwt, issuer, audience, resolveKey = getKey, ske
107
135
  if (postIssued) {
108
136
  return {
109
137
  valid: false,
110
- reason: `JWT issued at date claim (iat) is in the future. Issued at date: ${issuedAtDate}; Current date: ${currentDate};`
138
+ reason: `JWT issued at date claim (iat) is in the future. Issued at date: ${issuedAtDate}; Current date: ${currentDate};`,
139
+ reasonCode: 0,
140
+ decoded
111
141
  };
112
142
  }
113
143
  }
@@ -118,13 +148,17 @@ export async function parseJwt({ jwt, issuer, audience, resolveKey = getKey, ske
118
148
  catch (e) {
119
149
  return {
120
150
  valid: false,
121
- reason: `Error retrieving public key to verify JWT signature: ${e instanceof Error ? e.message : e}`
151
+ reason: `Error retrieving public key to verify JWT signature: ${e instanceof Error ? e.message : e}`,
152
+ reasonCode: 0,
153
+ decoded
122
154
  };
123
155
  }
124
156
  if (!key) {
125
157
  return {
126
158
  valid: false,
127
- reason: `Unable to resolve public key to verify JWT signature.`
159
+ reason: `Unable to resolve public key to verify JWT signature.`,
160
+ reasonCode: 0,
161
+ decoded
128
162
  };
129
163
  }
130
164
  let signatureValid;
@@ -132,10 +166,20 @@ export async function parseJwt({ jwt, issuer, audience, resolveKey = getKey, ske
132
166
  signatureValid = await verifyJwtSignature(decoded, key);
133
167
  }
134
168
  catch {
135
- return { valid: false, reason: `Error verifying JWT signature.` };
169
+ return {
170
+ valid: false,
171
+ reason: `Error verifying JWT signature.`,
172
+ reasonCode: 0,
173
+ decoded
174
+ };
136
175
  }
137
176
  if (!signatureValid) {
138
- return { valid: false, reason: `JWT signature is invalid.` };
177
+ return {
178
+ valid: false,
179
+ reason: `JWT signature is invalid.`,
180
+ reasonCode: 0,
181
+ decoded
182
+ };
139
183
  }
140
184
  const { header, payload } = decoded;
141
185
  return { valid: true, header, payload };
package/dist/types.d.ts CHANGED
@@ -35,6 +35,12 @@ export interface ValidJwtParseResult {
35
35
  export interface InvalidJwtParseResult {
36
36
  valid: false;
37
37
  reason: string;
38
+ reasonCode: InvalidJwtReasonCode;
39
+ decoded?: DecodedJwt;
40
+ }
41
+ export declare const enum InvalidJwtReasonCode {
42
+ Other = 0,
43
+ Expired = 1
38
44
  }
39
45
  export interface IssuerMetadata {
40
46
  issuer: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfworker/jwt",
3
- "version": "5.0.0",
3
+ "version": "5.1.0",
4
4
  "description": "Tiny lib for decoding JWTs and verifying signatures, using native crypto APIs",
5
5
  "keywords": [
6
6
  "jwt",
package/src/parse.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { algs, algToHash } from './algs.js';
2
2
  import { decodeJwt } from './decode.js';
3
3
  import { getKey } from './jwks.js';
4
- import { DecodedJwt, JwtParseResult } from './types.js';
4
+ import { DecodedJwt, InvalidJwtReasonCode, JwtParseResult } from './types.js';
5
5
  import { verifyJwtSignature } from './verify.js';
6
6
 
7
7
  const defaultSkewMs = 60 * 1000;
@@ -54,13 +54,19 @@ export async function parseJwt({
54
54
  try {
55
55
  decoded = decodeJwt(jwt);
56
56
  } catch {
57
- return { valid: false, reason: `Unable to decode JWT.` };
57
+ return {
58
+ valid: false,
59
+ reason: `Unable to decode JWT.`,
60
+ reasonCode: InvalidJwtReasonCode.Other
61
+ };
58
62
  }
59
63
  const { typ, alg } = decoded.header;
60
64
  if (typeof typ !== 'undefined' && typ !== 'JWT') {
61
65
  return {
62
66
  valid: false,
63
- reason: `Invalid JWT type ${JSON.stringify(typ)}. Expected "JWT".`
67
+ reason: `Invalid JWT type ${JSON.stringify(typ)}. Expected "JWT".`,
68
+ reasonCode: InvalidJwtReasonCode.Other,
69
+ decoded
64
70
  };
65
71
  }
66
72
  if (!algToHash[alg]) {
@@ -68,7 +74,9 @@ export async function parseJwt({
68
74
  valid: false,
69
75
  reason: `Invalid JWT algorithm ${JSON.stringify(
70
76
  alg
71
- )}. Supported: ${algs}.`
77
+ )}. Supported: ${algs}.`,
78
+ reasonCode: InvalidJwtReasonCode.Other,
79
+ decoded
72
80
  };
73
81
  }
74
82
 
@@ -78,7 +86,9 @@ export async function parseJwt({
78
86
  valid: false,
79
87
  reason: `Subject claim (sub) is required and must be a string. Received ${JSON.stringify(
80
88
  sub
81
- )}.`
89
+ )}.`,
90
+ reasonCode: InvalidJwtReasonCode.Other,
91
+ decoded
82
92
  };
83
93
  }
84
94
 
@@ -88,7 +98,9 @@ export async function parseJwt({
88
98
  valid: false,
89
99
  reason: `Invalid JWT audience claim (aud) ${JSON.stringify(
90
100
  aud
91
- )}. Expected "${audience}".`
101
+ )}. Expected "${audience}".`,
102
+ reasonCode: InvalidJwtReasonCode.Other,
103
+ decoded
92
104
  };
93
105
  }
94
106
  } else if (
@@ -101,7 +113,9 @@ export async function parseJwt({
101
113
  valid: false,
102
114
  reason: `Invalid JWT audience claim array (aud) ${JSON.stringify(
103
115
  aud
104
- )}. Does not include "${audience}".`
116
+ )}. Does not include "${audience}".`,
117
+ reasonCode: InvalidJwtReasonCode.Other,
118
+ decoded
105
119
  };
106
120
  }
107
121
  } else {
@@ -109,7 +123,9 @@ export async function parseJwt({
109
123
  valid: false,
110
124
  reason: `Invalid JWT audience claim (aud) ${JSON.stringify(
111
125
  aud
112
- )}. Expected a string or a non-empty array of strings.`
126
+ )}. Expected a string or a non-empty array of strings.`,
127
+ reasonCode: InvalidJwtReasonCode.Other,
128
+ decoded
113
129
  };
114
130
  }
115
131
 
@@ -118,7 +134,9 @@ export async function parseJwt({
118
134
  valid: false,
119
135
  reason: `Invalid JWT issuer claim (iss) ${JSON.stringify(
120
136
  decoded.payload.iss
121
- )}. Expected ${JSON.stringify(issuer)}.`
137
+ )}. Expected ${JSON.stringify(issuer)}.`,
138
+ reasonCode: InvalidJwtReasonCode.Other,
139
+ decoded
122
140
  };
123
141
  }
124
142
 
@@ -127,7 +145,9 @@ export async function parseJwt({
127
145
  valid: false,
128
146
  reason: `Invalid JWT expiry date claim (exp) ${JSON.stringify(
129
147
  exp
130
- )}. Expected number.`
148
+ )}. Expected number.`,
149
+ reasonCode: InvalidJwtReasonCode.Other,
150
+ decoded
131
151
  };
132
152
  }
133
153
  const currentDate = new Date(Date.now());
@@ -137,7 +157,9 @@ export async function parseJwt({
137
157
  if (expired) {
138
158
  return {
139
159
  valid: false,
140
- reason: `JWT is expired. Expiry date: ${expiryDate}; Current date: ${currentDate};`
160
+ reason: `JWT is expired. Expiry date: ${expiryDate}; Current date: ${currentDate};`,
161
+ reasonCode: InvalidJwtReasonCode.Expired,
162
+ decoded
141
163
  };
142
164
  }
143
165
 
@@ -147,7 +169,9 @@ export async function parseJwt({
147
169
  valid: false,
148
170
  reason: `Invalid JWT not before date claim (nbf) ${JSON.stringify(
149
171
  nbf
150
- )}. Expected number.`
172
+ )}. Expected number.`,
173
+ reasonCode: InvalidJwtReasonCode.Other,
174
+ decoded
151
175
  };
152
176
  }
153
177
  const notBeforeDate = new Date(0);
@@ -156,7 +180,9 @@ export async function parseJwt({
156
180
  if (early) {
157
181
  return {
158
182
  valid: false,
159
- reason: `JWT cannot be used prior to not before date claim (nbf). Not before date: ${notBeforeDate}; Current date: ${currentDate};`
183
+ reason: `JWT cannot be used prior to not before date claim (nbf). Not before date: ${notBeforeDate}; Current date: ${currentDate};`,
184
+ reasonCode: InvalidJwtReasonCode.Other,
185
+ decoded
160
186
  };
161
187
  }
162
188
  }
@@ -167,7 +193,9 @@ export async function parseJwt({
167
193
  valid: false,
168
194
  reason: `Invalid JWT issued at date claim (iat) ${JSON.stringify(
169
195
  iat
170
- )}. Expected number.`
196
+ )}. Expected number.`,
197
+ reasonCode: InvalidJwtReasonCode.Other,
198
+ decoded
171
199
  };
172
200
  }
173
201
  const issuedAtDate = new Date(0);
@@ -176,7 +204,9 @@ export async function parseJwt({
176
204
  if (postIssued) {
177
205
  return {
178
206
  valid: false,
179
- reason: `JWT issued at date claim (iat) is in the future. Issued at date: ${issuedAtDate}; Current date: ${currentDate};`
207
+ reason: `JWT issued at date claim (iat) is in the future. Issued at date: ${issuedAtDate}; Current date: ${currentDate};`,
208
+ reasonCode: InvalidJwtReasonCode.Other,
209
+ decoded
180
210
  };
181
211
  }
182
212
  }
@@ -189,23 +219,37 @@ export async function parseJwt({
189
219
  valid: false,
190
220
  reason: `Error retrieving public key to verify JWT signature: ${
191
221
  e instanceof Error ? e.message : e
192
- }`
222
+ }`,
223
+ reasonCode: InvalidJwtReasonCode.Other,
224
+ decoded
193
225
  };
194
226
  }
195
227
  if (!key) {
196
228
  return {
197
229
  valid: false,
198
- reason: `Unable to resolve public key to verify JWT signature.`
230
+ reason: `Unable to resolve public key to verify JWT signature.`,
231
+ reasonCode: InvalidJwtReasonCode.Other,
232
+ decoded
199
233
  };
200
234
  }
201
235
  let signatureValid: boolean;
202
236
  try {
203
237
  signatureValid = await verifyJwtSignature(decoded, key);
204
238
  } catch {
205
- return { valid: false, reason: `Error verifying JWT signature.` };
239
+ return {
240
+ valid: false,
241
+ reason: `Error verifying JWT signature.`,
242
+ reasonCode: InvalidJwtReasonCode.Other,
243
+ decoded
244
+ };
206
245
  }
207
246
  if (!signatureValid) {
208
- return { valid: false, reason: `JWT signature is invalid.` };
247
+ return {
248
+ valid: false,
249
+ reason: `JWT signature is invalid.`,
250
+ reasonCode: InvalidJwtReasonCode.Other,
251
+ decoded
252
+ };
209
253
  }
210
254
  const { header, payload } = decoded;
211
255
  return { valid: true, header, payload };
package/src/types.ts CHANGED
@@ -48,6 +48,13 @@ export interface ValidJwtParseResult {
48
48
  export interface InvalidJwtParseResult {
49
49
  valid: false;
50
50
  reason: string;
51
+ reasonCode: InvalidJwtReasonCode;
52
+ decoded?: DecodedJwt;
53
+ }
54
+
55
+ export const enum InvalidJwtReasonCode {
56
+ Other = 0,
57
+ Expired = 1
51
58
  }
52
59
 
53
60
  export interface IssuerMetadata {