@sourceregistry/node-jwt 1.3.1 β 1.4.1
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 +37 -1
- package/dist/index-BmAAEOLC.js +595 -0
- package/dist/index-BmAAEOLC.js.map +1 -0
- package/dist/index-eYY-I3Pd.cjs +2 -0
- package/dist/index-eYY-I3Pd.cjs.map +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +1 -1
- package/dist/jwt/index.d.ts +10 -0
- package/dist/jwt/promises.d.ts +38 -52
- package/dist/promises.cjs.js +1 -1
- package/dist/promises.cjs.js.map +1 -1
- package/dist/promises.es.js +37 -37
- package/dist/promises.es.js.map +1 -1
- package/package.json +5 -2
- package/dist/index-CSRWSLal.cjs +0 -2
- package/dist/index-CSRWSLal.cjs.map +0 -1
- package/dist/index-DyXdSqEc.js +0 -515
- package/dist/index-DyXdSqEc.js.map +0 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Most JWT libraries are bloated, have security pitfalls, or lack proper TypeScrip
|
|
|
14
14
|
* **Secure by default**
|
|
15
15
|
* **TypeScript-first** with full JSDoc
|
|
16
16
|
* **Zero external dependencies**
|
|
17
|
-
* **100% test coverage**
|
|
17
|
+
* **100% test coverage (Tryingπ)**
|
|
18
18
|
* **Dual API**: Sync and Promise-based
|
|
19
19
|
* **Automatic algorithm detection based on key type**
|
|
20
20
|
* **Full JWK/JWKS support** (`import/export`, `toPublicJWK`, `x5c/x5t`, RFC 7638 thumbprints, kid-based key selection)
|
|
@@ -166,6 +166,42 @@ const keyObject = JWKS.toKeyObject(jwks, jwk.kid);
|
|
|
166
166
|
|
|
167
167
|
---
|
|
168
168
|
|
|
169
|
+
## π ECDSA Signature Format: DER vs JOSE (New)
|
|
170
|
+
|
|
171
|
+
For **ECDSA** algorithms (`ES256`, `ES384`, `ES512`, `ES256K`) there are two common signature encodings:
|
|
172
|
+
|
|
173
|
+
- **DER** (ASN.1) β what Node.js produces by default
|
|
174
|
+
- **JOSE** (`r || s` raw signature) β required by the JWT/JWS spec and used by systems like **VAPID/Web Push (WNS)**
|
|
175
|
+
|
|
176
|
+
### Default behavior
|
|
177
|
+
By default, this library outputs **DER** signatures for `ES*` algorithms to match Node.js/OpenSSL defaults.
|
|
178
|
+
|
|
179
|
+
### Enable JOSE output
|
|
180
|
+
To generate spec-compliant JOSE ECDSA signatures, set:
|
|
181
|
+
|
|
182
|
+
- `signatureFormat: "jose"` in `sign()`
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { sign, verify } from "@sourceregistry/node-jwt";
|
|
186
|
+
|
|
187
|
+
const token = sign(
|
|
188
|
+
{ sub: "123", iat: Math.floor(Date.now() / 1000) },
|
|
189
|
+
ecPrivateKey,
|
|
190
|
+
{ alg: "ES256", signatureFormat: "jose" }
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// Verify JOSE-signed token
|
|
194
|
+
const result = verify(token, ecPublicKey, { signatureFormat: "jose" });
|
|
195
|
+
````
|
|
196
|
+
|
|
197
|
+
### Auto-detect verification (optional)
|
|
198
|
+
|
|
199
|
+
If enabled in your version, `verify()` can also validate JOSE ECDSA signatures without specifying `signatureFormat` (it will try DER first, then JOSE).
|
|
200
|
+
If you want strict behavior, pass `signatureFormat: "der"` or `signatureFormat: "jose"` explicitly.
|
|
201
|
+
|
|
202
|
+
> π‘ For VAPID/Web Push (e.g. Windows WNS endpoints), you typically need `ES256` with `signatureFormat: "jose"`.
|
|
203
|
+
|
|
204
|
+
|
|
169
205
|
## π API Reference
|
|
170
206
|
|
|
171
207
|
### `sign(payload, secret, options?)`
|
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
import p, { sign as R, createSign as l, createHmac as m, verify as x, createVerify as S, timingSafeEqual as O, createPrivateKey as I, createSecretKey as B, createPublicKey as T, createHash as K } from "crypto";
|
|
2
|
+
const v = {
|
|
3
|
+
encode: (e) => Buffer.from(e).toString("base64url"),
|
|
4
|
+
decode: (e) => Buffer.from(e, "base64url").toString()
|
|
5
|
+
}, b = (e, t) => e.length !== t.length ? !1 : O(Buffer.from(e), Buffer.from(t));
|
|
6
|
+
function P(e) {
|
|
7
|
+
switch (e) {
|
|
8
|
+
case "ES256":
|
|
9
|
+
case "ES256K":
|
|
10
|
+
return 64;
|
|
11
|
+
// 32 + 32
|
|
12
|
+
case "ES384":
|
|
13
|
+
return 96;
|
|
14
|
+
// 48 + 48
|
|
15
|
+
case "ES512":
|
|
16
|
+
return 132;
|
|
17
|
+
// 66 + 66 (P-521)
|
|
18
|
+
default:
|
|
19
|
+
throw new Error(`Unsupported ECDSA alg for JOSE conversion: ${e}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function H(e, t) {
|
|
23
|
+
let r = 0;
|
|
24
|
+
if (e[r++] !== 48) throw new Error("Invalid DER ECDSA signature");
|
|
25
|
+
let n = e[r++];
|
|
26
|
+
if (n & 128) {
|
|
27
|
+
const y = n & 127;
|
|
28
|
+
n = 0;
|
|
29
|
+
for (let o = 0; o < y; o++) n = n << 8 | e[r++];
|
|
30
|
+
}
|
|
31
|
+
if (e[r++] !== 2) throw new Error("Invalid DER ECDSA signature (r)");
|
|
32
|
+
const s = e[r++];
|
|
33
|
+
let a = e.subarray(r, r + s);
|
|
34
|
+
if (r += s, e[r++] !== 2) throw new Error("Invalid DER ECDSA signature (s)");
|
|
35
|
+
const f = e[r++];
|
|
36
|
+
let i = e.subarray(r, r + f);
|
|
37
|
+
for (; a.length > t / 2 && a[0] === 0; ) a = a.subarray(1);
|
|
38
|
+
for (; i.length > t / 2 && i[0] === 0; ) i = i.subarray(1);
|
|
39
|
+
const u = Buffer.concat([Buffer.alloc(t / 2 - a.length, 0), a]), c = Buffer.concat([Buffer.alloc(t / 2 - i.length, 0), i]);
|
|
40
|
+
return Buffer.concat([u, c]);
|
|
41
|
+
}
|
|
42
|
+
function E(e) {
|
|
43
|
+
const t = e.length / 2;
|
|
44
|
+
let r = e.subarray(0, t), n = e.subarray(t);
|
|
45
|
+
for (; r.length > 1 && r[0] === 0 && (r[1] & 128) === 0; ) r = r.subarray(1);
|
|
46
|
+
for (; n.length > 1 && n[0] === 0 && (n[1] & 128) === 0; ) n = n.subarray(1);
|
|
47
|
+
r[0] & 128 && (r = Buffer.concat([Buffer.from([0]), r])), n[0] & 128 && (n = Buffer.concat([Buffer.from([0]), n]));
|
|
48
|
+
const s = Buffer.concat([Buffer.from([2, r.length]), r]), a = Buffer.concat([Buffer.from([2, n.length]), n]), f = s.length + a.length;
|
|
49
|
+
let i;
|
|
50
|
+
if (f < 128)
|
|
51
|
+
i = Buffer.from([f]);
|
|
52
|
+
else {
|
|
53
|
+
const u = [];
|
|
54
|
+
let c = f;
|
|
55
|
+
for (; c > 0; )
|
|
56
|
+
u.unshift(c & 255), c >>= 8;
|
|
57
|
+
i = Buffer.from([128 | u.length, ...u]);
|
|
58
|
+
}
|
|
59
|
+
return Buffer.concat([Buffer.from([48]), i, s, a]);
|
|
60
|
+
}
|
|
61
|
+
function D(e) {
|
|
62
|
+
return e === "ES256" || e === "ES384" || e === "ES512" || e === "ES256K";
|
|
63
|
+
}
|
|
64
|
+
const g = {
|
|
65
|
+
// HMAC
|
|
66
|
+
HS256: {
|
|
67
|
+
sign: (e, t) => m("sha256", t).update(e).digest("base64url"),
|
|
68
|
+
verify: (e, t, r) => {
|
|
69
|
+
const n = m("sha256", t).update(e).digest("base64url");
|
|
70
|
+
return b(n, r);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
HS384: {
|
|
74
|
+
sign: (e, t) => m("sha384", t).update(e).digest("base64url"),
|
|
75
|
+
verify: (e, t, r) => {
|
|
76
|
+
const n = m("sha384", t).update(e).digest("base64url");
|
|
77
|
+
return b(n, r);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
HS512: {
|
|
81
|
+
sign: (e, t) => m("sha512", t).update(e).digest("base64url"),
|
|
82
|
+
verify: (e, t, r) => {
|
|
83
|
+
const n = m("sha512", t).update(e).digest("base64url");
|
|
84
|
+
return b(n, r);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
// RSA (DER-encoded signatures, base64url)
|
|
88
|
+
RS256: {
|
|
89
|
+
sign: (e, t) => l("RSA-SHA256").update(e).end().sign(t).toString("base64url"),
|
|
90
|
+
verify: (e, t, r) => {
|
|
91
|
+
try {
|
|
92
|
+
return S("RSA-SHA256").update(e).end().verify(t, Buffer.from(r, "base64url"));
|
|
93
|
+
} catch {
|
|
94
|
+
return !1;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
RS384: {
|
|
99
|
+
sign: (e, t) => l("RSA-SHA384").update(e).end().sign(t).toString("base64url"),
|
|
100
|
+
verify: (e, t, r) => {
|
|
101
|
+
try {
|
|
102
|
+
return S("RSA-SHA384").update(e).end().verify(t, Buffer.from(r, "base64url"));
|
|
103
|
+
} catch {
|
|
104
|
+
return !1;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
RS512: {
|
|
109
|
+
sign: (e, t) => l("RSA-SHA512").update(e).end().sign(t).toString("base64url"),
|
|
110
|
+
verify: (e, t, r) => {
|
|
111
|
+
try {
|
|
112
|
+
return S("RSA-SHA512").update(e).end().verify(t, Buffer.from(r, "base64url"));
|
|
113
|
+
} catch {
|
|
114
|
+
return !1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
// ECDSA (DER-encoded by default β no dsaEncoding!)
|
|
119
|
+
ES256: {
|
|
120
|
+
sign: (e, t) => l("SHA256").update(e).end().sign(t).toString("base64url"),
|
|
121
|
+
verify: (e, t, r) => {
|
|
122
|
+
try {
|
|
123
|
+
return S("SHA256").update(e).end().verify(t, Buffer.from(r, "base64url"));
|
|
124
|
+
} catch {
|
|
125
|
+
return !1;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
ES384: {
|
|
130
|
+
sign: (e, t) => l("SHA384").update(e).end().sign(t).toString("base64url"),
|
|
131
|
+
verify: (e, t, r) => {
|
|
132
|
+
try {
|
|
133
|
+
return S("SHA384").update(e).end().verify(t, Buffer.from(r, "base64url"));
|
|
134
|
+
} catch {
|
|
135
|
+
return !1;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
ES512: {
|
|
140
|
+
sign: (e, t) => l("SHA512").update(e).end().sign(t).toString("base64url"),
|
|
141
|
+
verify: (e, t, r) => {
|
|
142
|
+
try {
|
|
143
|
+
return S("SHA512").update(e).end().verify(t, Buffer.from(r, "base64url"));
|
|
144
|
+
} catch {
|
|
145
|
+
return !1;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
ES256K: {
|
|
150
|
+
sign: (e, t) => l("SHA256").update(e).end().sign(t).toString("base64url"),
|
|
151
|
+
verify: (e, t, r) => {
|
|
152
|
+
try {
|
|
153
|
+
return S("SHA256").update(e).end().verify(t, Buffer.from(r, "base64url"));
|
|
154
|
+
} catch {
|
|
155
|
+
return !1;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
PS256: {
|
|
160
|
+
sign: (e, t) => l("RSA-SHA256").update(e).end().sign({
|
|
161
|
+
//@ts-ignore
|
|
162
|
+
key: t,
|
|
163
|
+
padding: p.constants.RSA_PKCS1_PSS_PADDING,
|
|
164
|
+
saltLength: 32
|
|
165
|
+
}).toString("base64url"),
|
|
166
|
+
verify: (e, t, r) => {
|
|
167
|
+
try {
|
|
168
|
+
return S("RSA-SHA256").update(e).end().verify({
|
|
169
|
+
//@ts-ignore
|
|
170
|
+
key: t,
|
|
171
|
+
padding: p.constants.RSA_PKCS1_PSS_PADDING,
|
|
172
|
+
saltLength: 32
|
|
173
|
+
}, Buffer.from(r, "base64url"));
|
|
174
|
+
} catch {
|
|
175
|
+
return !1;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
PS384: {
|
|
180
|
+
sign: (e, t) => l("RSA-SHA384").update(e).end().sign({
|
|
181
|
+
//@ts-ignore
|
|
182
|
+
key: t,
|
|
183
|
+
padding: p.constants.RSA_PKCS1_PSS_PADDING,
|
|
184
|
+
saltLength: 48
|
|
185
|
+
}).toString("base64url"),
|
|
186
|
+
verify: (e, t, r) => {
|
|
187
|
+
try {
|
|
188
|
+
return S("RSA-SHA384").update(e).end().verify({
|
|
189
|
+
//@ts-ignore
|
|
190
|
+
key: t,
|
|
191
|
+
padding: p.constants.RSA_PKCS1_PSS_PADDING,
|
|
192
|
+
saltLength: 48
|
|
193
|
+
}, Buffer.from(r, "base64url"));
|
|
194
|
+
} catch {
|
|
195
|
+
return !1;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
PS512: {
|
|
200
|
+
sign: (e, t) => l("RSA-SHA512").update(e).end().sign({
|
|
201
|
+
//@ts-ignore
|
|
202
|
+
key: t,
|
|
203
|
+
padding: p.constants.RSA_PKCS1_PSS_PADDING,
|
|
204
|
+
saltLength: 64
|
|
205
|
+
}).toString("base64url"),
|
|
206
|
+
verify: (e, t, r) => {
|
|
207
|
+
try {
|
|
208
|
+
return S("RSA-SHA512").update(e).end().verify({
|
|
209
|
+
//@ts-ignore
|
|
210
|
+
key: t,
|
|
211
|
+
padding: p.constants.RSA_PKCS1_PSS_PADDING,
|
|
212
|
+
saltLength: 64
|
|
213
|
+
}, Buffer.from(r, "base64url"));
|
|
214
|
+
} catch {
|
|
215
|
+
return !1;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
EdDSA: {
|
|
220
|
+
sign: (e, t) => R(null, typeof e == "string" ? Buffer.from(e, "utf8") : e, t).toString("base64url"),
|
|
221
|
+
verify: (e, t, r) => {
|
|
222
|
+
try {
|
|
223
|
+
return x(
|
|
224
|
+
null,
|
|
225
|
+
typeof e == "string" ? Buffer.from(e, "utf8") : e,
|
|
226
|
+
t,
|
|
227
|
+
Buffer.from(r, "base64url")
|
|
228
|
+
);
|
|
229
|
+
} catch {
|
|
230
|
+
return !1;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}, F = Object.keys(g);
|
|
235
|
+
function $(e) {
|
|
236
|
+
if (e.type === "secret") return "HS256";
|
|
237
|
+
if (e.type !== "private") throw new Error("Only private or symmetric keys can be used to sign JWTs");
|
|
238
|
+
const t = e.asymmetricKeyType, r = e.asymmetricKeyDetails;
|
|
239
|
+
switch (t) {
|
|
240
|
+
case "rsa":
|
|
241
|
+
return "RS256";
|
|
242
|
+
case "rsa-pss": {
|
|
243
|
+
const n = r?.hashAlgorithm ?? "sha256";
|
|
244
|
+
switch (n) {
|
|
245
|
+
case "sha256":
|
|
246
|
+
return "PS256";
|
|
247
|
+
case "sha384":
|
|
248
|
+
return "PS384";
|
|
249
|
+
case "sha512":
|
|
250
|
+
return "PS512";
|
|
251
|
+
default:
|
|
252
|
+
throw new Error(`Unsupported RSA-PSS hash algorithm: ${n}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
case "ec": {
|
|
256
|
+
const n = r?.namedCurve;
|
|
257
|
+
switch (n) {
|
|
258
|
+
case "P-256":
|
|
259
|
+
case "prime256v1":
|
|
260
|
+
return "ES256";
|
|
261
|
+
case "P-384":
|
|
262
|
+
case "secp384r1":
|
|
263
|
+
return "ES384";
|
|
264
|
+
case "P-521":
|
|
265
|
+
case "secp521r1":
|
|
266
|
+
return "ES512";
|
|
267
|
+
case "secp256k1":
|
|
268
|
+
return "ES256K";
|
|
269
|
+
default:
|
|
270
|
+
throw new Error(`Unsupported EC curve: ${n}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
case "ed25519":
|
|
274
|
+
return "EdDSA";
|
|
275
|
+
default:
|
|
276
|
+
throw new Error(`Unsupported asymmetric key type: ${t}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function k(e) {
|
|
280
|
+
if (typeof e == "object" && "type" in e) return e;
|
|
281
|
+
try {
|
|
282
|
+
return I(e);
|
|
283
|
+
} catch {
|
|
284
|
+
const t = typeof e == "string" ? Buffer.from(e, "utf8") : Buffer.isBuffer(e) ? e : (() => {
|
|
285
|
+
throw new Error("Unsupported key type");
|
|
286
|
+
})();
|
|
287
|
+
return B(t);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const _ = (e) => {
|
|
291
|
+
const t = e.split(".");
|
|
292
|
+
if (t.length !== 3)
|
|
293
|
+
throw new Error('Invalid JWT: must contain exactly 3 parts separated by "."');
|
|
294
|
+
const [r, n, s] = t;
|
|
295
|
+
if (!r || !n || !s)
|
|
296
|
+
throw new Error("Invalid JWT: empty part detected");
|
|
297
|
+
try {
|
|
298
|
+
const a = JSON.parse(v.decode(r)), f = JSON.parse(v.decode(n));
|
|
299
|
+
return { header: a, payload: f, signature: s };
|
|
300
|
+
} catch (a) {
|
|
301
|
+
throw new Error(`Invalid JWT: malformed header or payload (${a.message})`);
|
|
302
|
+
}
|
|
303
|
+
}, W = (e, t, r = {}) => {
|
|
304
|
+
const n = k(t), s = r.alg ?? $(n), a = r.signatureFormat ?? "der", f = r.typ ?? "JWT";
|
|
305
|
+
if (!(s in g)) throw new Error(`Unsupported algorithm: ${s}`);
|
|
306
|
+
const i = { alg: s, typ: f };
|
|
307
|
+
r.kid && (i.kid = r.kid);
|
|
308
|
+
const u = v.encode(JSON.stringify(i)), c = v.encode(JSON.stringify(e)), y = `${u}.${c}`;
|
|
309
|
+
let o = g[s].sign(y, t);
|
|
310
|
+
if (a === "jose" && D(s)) {
|
|
311
|
+
const d = Buffer.from(o, "base64url");
|
|
312
|
+
o = H(d, P(s)).toString("base64url");
|
|
313
|
+
}
|
|
314
|
+
return `${u}.${c}.${o}`;
|
|
315
|
+
}, U = (e, t, r = {}) => {
|
|
316
|
+
let n;
|
|
317
|
+
try {
|
|
318
|
+
n = _(e);
|
|
319
|
+
} catch (o) {
|
|
320
|
+
return {
|
|
321
|
+
valid: !1,
|
|
322
|
+
error: {
|
|
323
|
+
reason: o.message,
|
|
324
|
+
code: "INVALID_TOKEN"
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
const { header: s, payload: a, signature: f } = n, i = s.alg;
|
|
329
|
+
if (!(i in g))
|
|
330
|
+
return {
|
|
331
|
+
valid: !1,
|
|
332
|
+
error: {
|
|
333
|
+
reason: `Unsupported or unknown algorithm: ${s.alg}`,
|
|
334
|
+
code: "INVALID_ALGORITHM"
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
if (r.algorithms && r.algorithms.length > 0 && !r.algorithms.includes(i))
|
|
338
|
+
return {
|
|
339
|
+
valid: !1,
|
|
340
|
+
error: {
|
|
341
|
+
reason: `Algorithm "${i}" is not in the allowed algorithms list`,
|
|
342
|
+
code: "ALGORITHM_NOT_ALLOWED"
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
if (s.typ !== void 0 && s.typ !== "JWT")
|
|
346
|
+
return {
|
|
347
|
+
valid: !1,
|
|
348
|
+
error: {
|
|
349
|
+
reason: `Invalid token type: expected 'JWT', got '${s.typ}'`,
|
|
350
|
+
code: "INVALID_TYPE"
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
const u = `${v.encode(JSON.stringify(s))}.${v.encode(JSON.stringify(a))}`;
|
|
354
|
+
if (D(i)) {
|
|
355
|
+
const o = r.signatureFormat;
|
|
356
|
+
let d;
|
|
357
|
+
if (o === "jose")
|
|
358
|
+
try {
|
|
359
|
+
const h = Buffer.from(f, "base64url"), A = E(h).toString("base64url");
|
|
360
|
+
d = g[i].verify(u, t, A);
|
|
361
|
+
} catch {
|
|
362
|
+
d = !1;
|
|
363
|
+
}
|
|
364
|
+
else if (o === "der")
|
|
365
|
+
d = g[i].verify(u, t, f);
|
|
366
|
+
else if (d = g[i].verify(u, t, f), !d)
|
|
367
|
+
try {
|
|
368
|
+
const h = Buffer.from(f, "base64url");
|
|
369
|
+
if (h.length === P(i)) {
|
|
370
|
+
const A = E(h).toString("base64url");
|
|
371
|
+
d = g[i].verify(u, t, A);
|
|
372
|
+
}
|
|
373
|
+
} catch {
|
|
374
|
+
}
|
|
375
|
+
if (!d)
|
|
376
|
+
return { valid: !1, error: { reason: "Signature verification failed", code: "INVALID_SIGNATURE" } };
|
|
377
|
+
} else if (!g[i].verify(u, t, f))
|
|
378
|
+
return { valid: !1, error: { reason: "Signature verification failed", code: "INVALID_SIGNATURE" } };
|
|
379
|
+
const c = Math.floor(Date.now() / 1e3), y = r.clockSkew ?? 0;
|
|
380
|
+
if (!r.ignoreExpiration && a.exp !== void 0 && c > a.exp + y)
|
|
381
|
+
return {
|
|
382
|
+
valid: !1,
|
|
383
|
+
error: {
|
|
384
|
+
reason: "Token expired",
|
|
385
|
+
code: "TOKEN_EXPIRED"
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
if (a.nbf !== void 0 && c + y < a.nbf)
|
|
389
|
+
return {
|
|
390
|
+
valid: !1,
|
|
391
|
+
error: {
|
|
392
|
+
reason: "Token not yet valid",
|
|
393
|
+
code: "TOKEN_NOT_ACTIVE"
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
if (a.iat !== void 0 && c + y < a.iat)
|
|
397
|
+
return {
|
|
398
|
+
valid: !1,
|
|
399
|
+
error: {
|
|
400
|
+
reason: "Token issued in the future",
|
|
401
|
+
code: "TOKEN_FUTURE_ISSUED"
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
if (r.maxTokenAge !== void 0 && a.iat !== void 0) {
|
|
405
|
+
const o = c - a.iat;
|
|
406
|
+
if (o > r.maxTokenAge)
|
|
407
|
+
return {
|
|
408
|
+
valid: !1,
|
|
409
|
+
error: {
|
|
410
|
+
reason: `Token age (${o}s) exceeds maximum allowed age (${r.maxTokenAge}s)`,
|
|
411
|
+
code: "TOKEN_TOO_OLD"
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
if (r.issuer !== void 0) {
|
|
416
|
+
if (a.iss === void 0)
|
|
417
|
+
return {
|
|
418
|
+
valid: !1,
|
|
419
|
+
error: {
|
|
420
|
+
reason: 'Token missing required issuer claim ("iss")',
|
|
421
|
+
code: "MISSING_ISSUER"
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
if (r.issuer !== a.iss)
|
|
425
|
+
return {
|
|
426
|
+
valid: !1,
|
|
427
|
+
error: {
|
|
428
|
+
reason: `Invalid token issuer: expected "${r.issuer}", got "${a.iss}"`,
|
|
429
|
+
code: "INVALID_ISSUER"
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
if (r.subject !== void 0) {
|
|
434
|
+
if (a.sub === void 0)
|
|
435
|
+
return {
|
|
436
|
+
valid: !1,
|
|
437
|
+
error: {
|
|
438
|
+
reason: 'Token missing required subject claim ("sub")',
|
|
439
|
+
code: "MISSING_SUBJECT"
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
if (r.subject !== a.sub)
|
|
443
|
+
return {
|
|
444
|
+
valid: !1,
|
|
445
|
+
error: {
|
|
446
|
+
reason: `Invalid token subject: expected "${r.subject}", got "${a.sub}"`,
|
|
447
|
+
code: "INVALID_SUBJECT"
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
if (r.audience !== void 0) {
|
|
452
|
+
const o = a.aud;
|
|
453
|
+
if (o === void 0)
|
|
454
|
+
return {
|
|
455
|
+
valid: !1,
|
|
456
|
+
error: {
|
|
457
|
+
reason: 'Token missing required audience claim ("aud")',
|
|
458
|
+
code: "MISSING_AUDIENCE"
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
const d = Array.isArray(r.audience) ? r.audience : [r.audience], h = Array.isArray(o) ? o : [o];
|
|
462
|
+
if (!d.some((N) => h.includes(N)))
|
|
463
|
+
return {
|
|
464
|
+
valid: !1,
|
|
465
|
+
error: {
|
|
466
|
+
reason: "Audience claim mismatch",
|
|
467
|
+
code: "INVALID_AUDIENCE"
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
if (r.jwtId !== void 0) {
|
|
472
|
+
if (a.jti === void 0)
|
|
473
|
+
return {
|
|
474
|
+
valid: !1,
|
|
475
|
+
error: {
|
|
476
|
+
reason: 'Token missing required JWT ID claim ("jti")',
|
|
477
|
+
code: "MISSING_JTI"
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
if (r.jwtId !== a.jti)
|
|
481
|
+
return {
|
|
482
|
+
valid: !1,
|
|
483
|
+
error: {
|
|
484
|
+
reason: `Invalid JWT ID: expected "${r.jwtId}", got "${a.jti}"`,
|
|
485
|
+
code: "INVALID_JTI"
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
return { valid: !0, header: s, payload: a, signature: f };
|
|
490
|
+
}, j = {
|
|
491
|
+
sign: W,
|
|
492
|
+
verify: U,
|
|
493
|
+
decode: _,
|
|
494
|
+
algorithms: g
|
|
495
|
+
};
|
|
496
|
+
function L(e) {
|
|
497
|
+
if (!e || typeof e != "object") throw new Error("Invalid KeyObject");
|
|
498
|
+
return e.export({ format: "jwk" });
|
|
499
|
+
}
|
|
500
|
+
function J(e) {
|
|
501
|
+
if (!e || typeof e != "object") throw new Error("Invalid JWK");
|
|
502
|
+
switch (e.kty) {
|
|
503
|
+
case "oct": {
|
|
504
|
+
if (!("k" in e) || typeof e.k != "string")
|
|
505
|
+
throw new Error('Invalid oct JWK: missing "k"');
|
|
506
|
+
return B(Buffer.from(e.k, "base64url"));
|
|
507
|
+
}
|
|
508
|
+
case "RSA":
|
|
509
|
+
case "EC":
|
|
510
|
+
case "OKP":
|
|
511
|
+
return "d" in e && typeof e.d == "string" ? I({ format: "jwk", key: e }) : T({ format: "jwk", key: e });
|
|
512
|
+
default:
|
|
513
|
+
throw new Error(`Unsupported JWK key type: ${e.kty}`);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
function C(e) {
|
|
517
|
+
if (!e || typeof e != "object")
|
|
518
|
+
throw new Error("Invalid KeyObject");
|
|
519
|
+
const r = (e.type === "private" ? T(e) : e).export({ format: "jwk" });
|
|
520
|
+
return delete r.d, delete r.p, delete r.q, delete r.dp, delete r.dq, delete r.qi, r;
|
|
521
|
+
}
|
|
522
|
+
function w(e, t = "sha256") {
|
|
523
|
+
if (!e || typeof e != "object")
|
|
524
|
+
throw new Error("Invalid JWK");
|
|
525
|
+
let r;
|
|
526
|
+
switch (e.kty) {
|
|
527
|
+
case "RSA":
|
|
528
|
+
r = { e: e.e, kty: e.kty, n: e.n };
|
|
529
|
+
break;
|
|
530
|
+
case "EC":
|
|
531
|
+
r = { crv: e.crv, kty: e.kty, x: e.x, y: e.y };
|
|
532
|
+
break;
|
|
533
|
+
case "OKP":
|
|
534
|
+
r = { crv: e.crv, kty: e.kty, x: e.x };
|
|
535
|
+
break;
|
|
536
|
+
case "oct":
|
|
537
|
+
r = { k: e.k, kty: e.kty };
|
|
538
|
+
break;
|
|
539
|
+
default:
|
|
540
|
+
throw new Error(`Unsupported JWK key type: ${e.kty}`);
|
|
541
|
+
}
|
|
542
|
+
const n = JSON.stringify(
|
|
543
|
+
Object.keys(r).sort().reduce((s, a) => (s[a] = r[a], s), {})
|
|
544
|
+
);
|
|
545
|
+
return K(t).update(n).digest("base64url");
|
|
546
|
+
}
|
|
547
|
+
function G(e) {
|
|
548
|
+
if (e.x5c?.length)
|
|
549
|
+
return K("sha1").update(Buffer.from(e.x5c[0], "base64")).digest("base64url");
|
|
550
|
+
}
|
|
551
|
+
const z = {
|
|
552
|
+
export: L,
|
|
553
|
+
import: J,
|
|
554
|
+
toPublic: C,
|
|
555
|
+
thumbprint: w
|
|
556
|
+
};
|
|
557
|
+
function V(e, t) {
|
|
558
|
+
if (!e || !Array.isArray(e.keys)) throw new Error("Invalid JWKS");
|
|
559
|
+
let r;
|
|
560
|
+
if (t && (r = e.keys.find((n) => n.kid === t)), !r && e.keys.length === 1 && (r = e.keys[0]), !r) throw new Error("Key not found in JWKS");
|
|
561
|
+
return J(r);
|
|
562
|
+
}
|
|
563
|
+
function q(e) {
|
|
564
|
+
return {
|
|
565
|
+
keys: e.keys.map((t) => ({
|
|
566
|
+
...t,
|
|
567
|
+
kid: t.kid ?? w(t),
|
|
568
|
+
x5t: t.x5t ?? G(t)
|
|
569
|
+
}))
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
const X = {
|
|
573
|
+
toKeyObject: V,
|
|
574
|
+
normalize: q
|
|
575
|
+
};
|
|
576
|
+
export {
|
|
577
|
+
$ as A,
|
|
578
|
+
j as J,
|
|
579
|
+
g as S,
|
|
580
|
+
F as a,
|
|
581
|
+
v as b,
|
|
582
|
+
G as c,
|
|
583
|
+
_ as d,
|
|
584
|
+
L as e,
|
|
585
|
+
z as f,
|
|
586
|
+
w as g,
|
|
587
|
+
V as h,
|
|
588
|
+
J as i,
|
|
589
|
+
X as j,
|
|
590
|
+
q as n,
|
|
591
|
+
W as s,
|
|
592
|
+
C as t,
|
|
593
|
+
U as v
|
|
594
|
+
};
|
|
595
|
+
//# sourceMappingURL=index-BmAAEOLC.js.map
|