@pagopa/io-wallet-utils 1.5.8 → 2.0.0-launchpad
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/index.d.mts +609 -61
- package/dist/index.d.ts +609 -61
- package/dist/index.js +661 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +628 -42
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -4
package/dist/index.js
CHANGED
|
@@ -32,41 +32,57 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
CLOCK_SKEW_TOLERANCE_SECONDS: () => CLOCK_SKEW_TOLERANCE_SECONDS,
|
|
34
34
|
CONTENT_TYPES: () => CONTENT_TYPES,
|
|
35
|
-
ContentType: () =>
|
|
35
|
+
ContentType: () => ContentType,
|
|
36
36
|
HEADERS: () => HEADERS,
|
|
37
|
-
HashAlgorithm: () =>
|
|
37
|
+
HashAlgorithm: () => HashAlgorithm,
|
|
38
38
|
IoWalletSdkConfig: () => IoWalletSdkConfig,
|
|
39
39
|
ItWalletSpecsVersion: () => ItWalletSpecsVersion,
|
|
40
40
|
ItWalletSpecsVersionError: () => ItWalletSpecsVersionError,
|
|
41
|
-
JsonParseError: () =>
|
|
41
|
+
JsonParseError: () => JsonParseError,
|
|
42
|
+
JwtParseError: () => JwtParseError,
|
|
43
|
+
JwtVerificationError: () => JwtVerificationError,
|
|
42
44
|
MAX_IAT_AGE_SECONDS: () => MAX_IAT_AGE_SECONDS,
|
|
45
|
+
MAX_JTI_LENGTH: () => MAX_JTI_LENGTH,
|
|
43
46
|
UnexpectedStatusCodeError: () => UnexpectedStatusCodeError,
|
|
44
47
|
ValidationError: () => ValidationError,
|
|
45
|
-
addSecondsToDate: () =>
|
|
46
|
-
calculateJwkThumbprint: () =>
|
|
47
|
-
createFetcher: () =>
|
|
48
|
+
addSecondsToDate: () => addSecondsToDate,
|
|
49
|
+
calculateJwkThumbprint: () => calculateJwkThumbprint,
|
|
50
|
+
createFetcher: () => createFetcher,
|
|
48
51
|
createVersionDispatcher: () => createVersionDispatcher,
|
|
49
|
-
dateToSeconds: () =>
|
|
50
|
-
decodeBase64: () =>
|
|
51
|
-
decodeJwt: () =>
|
|
52
|
-
|
|
52
|
+
dateToSeconds: () => dateToSeconds,
|
|
53
|
+
decodeBase64: () => decodeBase64,
|
|
54
|
+
decodeJwt: () => decodeJwt,
|
|
55
|
+
decodeJwtHeader: () => decodeJwtHeader,
|
|
56
|
+
decodeUtf8String: () => decodeUtf8String,
|
|
53
57
|
dispatchByVersion: () => dispatchByVersion,
|
|
54
|
-
encodeToBase64Url: () =>
|
|
55
|
-
encodeToUtf8String: () =>
|
|
58
|
+
encodeToBase64Url: () => encodeToBase64Url,
|
|
59
|
+
encodeToUtf8String: () => encodeToUtf8String,
|
|
56
60
|
formatError: () => formatError,
|
|
57
61
|
formatZodError: () => formatZodError,
|
|
58
62
|
hasConfigVersion: () => hasConfigVersion,
|
|
59
63
|
hasStatusOrThrow: () => hasStatusOrThrow,
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
jwtHeaderFromJwtSigner: () => jwtHeaderFromJwtSigner,
|
|
65
|
+
jwtSignerFromJwt: () => jwtSignerFromJwt,
|
|
66
|
+
objectToQueryParams: () => objectToQueryParams,
|
|
67
|
+
parseIfJson: () => parseIfJson,
|
|
62
68
|
parseRawHttpResponse: () => parseRawHttpResponse,
|
|
63
69
|
parseWithErrorHandling: () => parseWithErrorHandling,
|
|
64
70
|
serializeAttrs: () => serializeAttrs,
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
stringToJsonWithErrorHandling: () => stringToJsonWithErrorHandling,
|
|
72
|
+
verifyJwt: () => verifyJwt,
|
|
67
73
|
verifyJwtIatOrThrow: () => verifyJwtIatOrThrow,
|
|
74
|
+
zAlgValueNotNone: () => zAlgValueNotNone,
|
|
75
|
+
zCertificateChain: () => zCertificateChain,
|
|
76
|
+
zCompactJwe: () => zCompactJwe,
|
|
77
|
+
zCompactJwt: () => zCompactJwt,
|
|
68
78
|
zHttpMethod: () => zHttpMethod,
|
|
69
|
-
zItwSupportedSignatureAlg: () => zItwSupportedSignatureAlg
|
|
79
|
+
zItwSupportedSignatureAlg: () => zItwSupportedSignatureAlg,
|
|
80
|
+
zJwk: () => zJwk,
|
|
81
|
+
zJwkSet: () => zJwkSet,
|
|
82
|
+
zJwtConfirmationPayload: () => zJwtConfirmationPayload,
|
|
83
|
+
zJwtHeader: () => zJwtHeader,
|
|
84
|
+
zJwtPayload: () => zJwtPayload,
|
|
85
|
+
zTrustChain: () => zTrustChain
|
|
70
86
|
});
|
|
71
87
|
module.exports = __toCommonJS(index_exports);
|
|
72
88
|
|
|
@@ -120,6 +136,59 @@ var HEADERS = {
|
|
|
120
136
|
var MAX_IAT_AGE_SECONDS = 5 * 60;
|
|
121
137
|
var CLOCK_SKEW_TOLERANCE_SECONDS = 60;
|
|
122
138
|
|
|
139
|
+
// src/content-type.ts
|
|
140
|
+
var ContentType = /* @__PURE__ */ ((ContentType2) => {
|
|
141
|
+
ContentType2["Html"] = "text/html";
|
|
142
|
+
ContentType2["Json"] = "application/json";
|
|
143
|
+
ContentType2["JwkSet"] = "application/jwk-set+json";
|
|
144
|
+
ContentType2["Jwt"] = "application/jwt";
|
|
145
|
+
ContentType2["OAuthAuthorizationRequestJwt"] = "application/oauth-authz-req+jwt";
|
|
146
|
+
ContentType2["XWwwFormUrlencoded"] = "application/x-www-form-urlencoded";
|
|
147
|
+
return ContentType2;
|
|
148
|
+
})(ContentType || {});
|
|
149
|
+
|
|
150
|
+
// src/crypto/hash.ts
|
|
151
|
+
var HashAlgorithm = /* @__PURE__ */ ((HashAlgorithm2) => {
|
|
152
|
+
HashAlgorithm2["Sha256"] = "sha-256";
|
|
153
|
+
HashAlgorithm2["Sha384"] = "sha-384";
|
|
154
|
+
HashAlgorithm2["Sha512"] = "sha-512";
|
|
155
|
+
return HashAlgorithm2;
|
|
156
|
+
})(HashAlgorithm || {});
|
|
157
|
+
|
|
158
|
+
// src/encoding.ts
|
|
159
|
+
var utf8Encoder = new TextEncoder();
|
|
160
|
+
var utf8Decoder = new TextDecoder();
|
|
161
|
+
function base64UrlToBase64(base64) {
|
|
162
|
+
const normalized = base64.replace(/-/g, "+").replace(/_/g, "/");
|
|
163
|
+
const paddingLength = normalized.length % 4;
|
|
164
|
+
return paddingLength === 0 ? normalized : normalized.padEnd(normalized.length + (4 - paddingLength), "=");
|
|
165
|
+
}
|
|
166
|
+
function base64ToBase64Url(base64) {
|
|
167
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
168
|
+
}
|
|
169
|
+
function encodeBase64(data) {
|
|
170
|
+
let binary = "";
|
|
171
|
+
for (const byte of data) {
|
|
172
|
+
binary += String.fromCharCode(byte);
|
|
173
|
+
}
|
|
174
|
+
return btoa(binary);
|
|
175
|
+
}
|
|
176
|
+
function decodeUtf8String(value) {
|
|
177
|
+
return utf8Encoder.encode(value);
|
|
178
|
+
}
|
|
179
|
+
function encodeToUtf8String(data) {
|
|
180
|
+
return utf8Decoder.decode(data);
|
|
181
|
+
}
|
|
182
|
+
function decodeBase64(base64) {
|
|
183
|
+
const binary = atob(base64UrlToBase64(base64));
|
|
184
|
+
return Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
185
|
+
}
|
|
186
|
+
function encodeToBase64Url(data) {
|
|
187
|
+
return base64ToBase64Url(
|
|
188
|
+
encodeBase64(typeof data === "string" ? decodeUtf8String(data) : data)
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
123
192
|
// src/errors/parse.ts
|
|
124
193
|
var constants = {
|
|
125
194
|
identifierRegex: /[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*/u,
|
|
@@ -178,6 +247,27 @@ function formatZodError(error) {
|
|
|
178
247
|
}
|
|
179
248
|
|
|
180
249
|
// src/errors/errors.ts
|
|
250
|
+
var JsonParseError = class extends Error {
|
|
251
|
+
constructor(message, jsonString) {
|
|
252
|
+
super(`${message}
|
|
253
|
+
${jsonString}`);
|
|
254
|
+
this.name = "JsonParseError";
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
var JwtParseError = class extends Error {
|
|
258
|
+
constructor(message) {
|
|
259
|
+
super(message ?? "Error parsing jwt");
|
|
260
|
+
this.name = "JwtParseError";
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
var JwtVerificationError = class extends Error {
|
|
264
|
+
constructor(message, options) {
|
|
265
|
+
const errorMessage = message ?? "Error verifying jwt.";
|
|
266
|
+
const causeMessage = options?.cause instanceof Error ? ` ${options.cause.message}` : options?.cause ? ` ${String(options.cause)}` : "";
|
|
267
|
+
super(`${errorMessage}${causeMessage}`, options);
|
|
268
|
+
this.name = "JwtVerificationError";
|
|
269
|
+
}
|
|
270
|
+
};
|
|
181
271
|
var serializeAttrs = (attrs) => Object.entries(attrs).filter(([, v]) => v !== void 0).map(([k, v]) => {
|
|
182
272
|
if (Array.isArray(v)) return [k, `(${v.join(", ")})`];
|
|
183
273
|
if (typeof v !== "string") return [k, JSON.stringify(v)];
|
|
@@ -232,21 +322,6 @@ ${formattedError}`;
|
|
|
232
322
|
}
|
|
233
323
|
};
|
|
234
324
|
|
|
235
|
-
// src/fetcher.ts
|
|
236
|
-
var hasStatusOrThrow = (status, customError) => async (res) => {
|
|
237
|
-
if (Array.isArray(status) ? !status.includes(res.status) : res.status !== status) {
|
|
238
|
-
const ErrorClass = customError ?? UnexpectedStatusCodeError;
|
|
239
|
-
throw new ErrorClass({
|
|
240
|
-
message: `Http request failed. Expected ${status}, got ${res.status}, url: ${res.url}`,
|
|
241
|
-
reason: await parseRawHttpResponse(res),
|
|
242
|
-
// Pass the response body as reason so the original error can surface
|
|
243
|
-
statusCode: res.status
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
return res;
|
|
247
|
-
};
|
|
248
|
-
var parseRawHttpResponse = (response) => response.headers.get("content-type")?.includes("application/json") ? response.json() : response.text();
|
|
249
|
-
|
|
250
325
|
// src/parse.ts
|
|
251
326
|
var SAFE_STRINGIFY_MAX_LENGTH = 200;
|
|
252
327
|
function safeStringify(value) {
|
|
@@ -263,6 +338,26 @@ function safeStringify(value) {
|
|
|
263
338
|
return "[unserializable]";
|
|
264
339
|
}
|
|
265
340
|
}
|
|
341
|
+
function stringToJsonWithErrorHandling(value, errorMessage) {
|
|
342
|
+
try {
|
|
343
|
+
return JSON.parse(value);
|
|
344
|
+
} catch {
|
|
345
|
+
throw new JsonParseError(
|
|
346
|
+
errorMessage ?? "Unable to parse string to JSON.",
|
|
347
|
+
value
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
function parseIfJson(data) {
|
|
352
|
+
if (typeof data !== "string") {
|
|
353
|
+
return data;
|
|
354
|
+
}
|
|
355
|
+
try {
|
|
356
|
+
return JSON.parse(data);
|
|
357
|
+
} catch {
|
|
358
|
+
return data;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
266
361
|
function parseWithErrorHandling(schema, data, customErrorMessage) {
|
|
267
362
|
const parseResult = schema.safeParse(data);
|
|
268
363
|
if (!parseResult.success) {
|
|
@@ -277,9 +372,522 @@ function formatError(message, prefix) {
|
|
|
277
372
|
return prefix ? `${prefix} ${message}` : message;
|
|
278
373
|
}
|
|
279
374
|
|
|
280
|
-
// src/
|
|
375
|
+
// src/crypto/jwk/z-jwk.ts
|
|
376
|
+
var import_zod2 = __toESM(require("zod"));
|
|
377
|
+
|
|
378
|
+
// src/crypto/z-common.ts
|
|
281
379
|
var import_zod = __toESM(require("zod"));
|
|
282
|
-
var
|
|
380
|
+
var zTrustChain = import_zod.default.tuple([import_zod.default.string()], import_zod.default.string());
|
|
381
|
+
var zCertificateChain = import_zod.default.array(import_zod.default.string()).nonempty();
|
|
382
|
+
var zAlgValueNotNone = import_zod.default.string().refine((alg) => alg !== "none", { message: `alg value may not be 'none'` });
|
|
383
|
+
|
|
384
|
+
// src/crypto/jwk/z-jwk.ts
|
|
385
|
+
var zJwk = import_zod2.default.looseObject({
|
|
386
|
+
alg: import_zod2.default.optional(import_zod2.default.string()),
|
|
387
|
+
crv: import_zod2.default.optional(import_zod2.default.string()),
|
|
388
|
+
d: import_zod2.default.optional(import_zod2.default.string()),
|
|
389
|
+
dp: import_zod2.default.optional(import_zod2.default.string()),
|
|
390
|
+
dq: import_zod2.default.optional(import_zod2.default.string()),
|
|
391
|
+
e: import_zod2.default.optional(import_zod2.default.string()),
|
|
392
|
+
ext: import_zod2.default.optional(import_zod2.default.boolean()),
|
|
393
|
+
k: import_zod2.default.optional(import_zod2.default.string()),
|
|
394
|
+
key_ops: import_zod2.default.optional(import_zod2.default.array(import_zod2.default.string())),
|
|
395
|
+
kid: import_zod2.default.optional(import_zod2.default.string()),
|
|
396
|
+
kty: import_zod2.default.string(),
|
|
397
|
+
n: import_zod2.default.optional(import_zod2.default.string()),
|
|
398
|
+
oth: import_zod2.default.optional(
|
|
399
|
+
import_zod2.default.array(
|
|
400
|
+
import_zod2.default.looseObject({
|
|
401
|
+
d: import_zod2.default.optional(import_zod2.default.string()),
|
|
402
|
+
r: import_zod2.default.optional(import_zod2.default.string()),
|
|
403
|
+
t: import_zod2.default.optional(import_zod2.default.string())
|
|
404
|
+
})
|
|
405
|
+
)
|
|
406
|
+
),
|
|
407
|
+
p: import_zod2.default.optional(import_zod2.default.string()),
|
|
408
|
+
q: import_zod2.default.optional(import_zod2.default.string()),
|
|
409
|
+
qi: import_zod2.default.optional(import_zod2.default.string()),
|
|
410
|
+
use: import_zod2.default.optional(import_zod2.default.string()),
|
|
411
|
+
x: import_zod2.default.optional(import_zod2.default.string()),
|
|
412
|
+
x5c: import_zod2.default.optional(zCertificateChain),
|
|
413
|
+
x5t: import_zod2.default.optional(import_zod2.default.string()),
|
|
414
|
+
"x5t#S256": import_zod2.default.optional(import_zod2.default.string()),
|
|
415
|
+
x5u: import_zod2.default.optional(import_zod2.default.string()),
|
|
416
|
+
y: import_zod2.default.optional(import_zod2.default.string())
|
|
417
|
+
});
|
|
418
|
+
var zJwkSet = import_zod2.default.looseObject({ keys: import_zod2.default.array(zJwk) });
|
|
419
|
+
|
|
420
|
+
// src/crypto/jwk/calculate-jwk-thumbprint.ts
|
|
421
|
+
function createJwkThumbprintComponents(jwk) {
|
|
422
|
+
if (jwk.kty === "EC") {
|
|
423
|
+
if (!jwk.crv || !jwk.x || !jwk.y) {
|
|
424
|
+
throw new ValidationError(
|
|
425
|
+
"Provided jwk does not match a supported jwk structure. Either the 'kty' is not supported, or required values are missing."
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
crv: jwk.crv,
|
|
430
|
+
kty: jwk.kty,
|
|
431
|
+
x: jwk.x,
|
|
432
|
+
y: jwk.y
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
if (jwk.kty === "RSA") {
|
|
436
|
+
if (!jwk.e || !jwk.n) {
|
|
437
|
+
throw new ValidationError(
|
|
438
|
+
"Provided jwk does not match a supported jwk structure. Either the 'kty' is not supported, or required values are missing."
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
e: jwk.e,
|
|
443
|
+
kty: jwk.kty,
|
|
444
|
+
n: jwk.n
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
if (jwk.kty === "oct") {
|
|
448
|
+
if (!jwk.k) {
|
|
449
|
+
throw new ValidationError(
|
|
450
|
+
"Provided jwk does not match a supported jwk structure. Either the 'kty' is not supported, or required values are missing."
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
return {
|
|
454
|
+
k: jwk.k,
|
|
455
|
+
kty: jwk.kty
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
throw new ValidationError(
|
|
459
|
+
"Provided jwk does not match a supported jwk structure. Either the 'kty' is not supported, or required values are missing."
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
async function calculateJwkThumbprint(options) {
|
|
463
|
+
const parsedJwk = parseWithErrorHandling(
|
|
464
|
+
zJwk,
|
|
465
|
+
options.jwk,
|
|
466
|
+
"Provided jwk does not match a supported jwk structure. Either the 'kty' is not supported, or required values are missing."
|
|
467
|
+
);
|
|
468
|
+
const jwkThumbprintComponents = createJwkThumbprintComponents(parsedJwk);
|
|
469
|
+
return encodeToBase64Url(
|
|
470
|
+
await options.hashCallback(
|
|
471
|
+
decodeUtf8String(JSON.stringify(jwkThumbprintComponents)),
|
|
472
|
+
options.hashAlgorithm
|
|
473
|
+
)
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// src/crypto/jwt/z-jwt.ts
|
|
478
|
+
var import_zod3 = __toESM(require("zod"));
|
|
479
|
+
var MAX_JTI_LENGTH = 256;
|
|
480
|
+
var zCompactJwt = import_zod3.default.string().regex(/^([a-zA-Z0-9-_]+)\.([a-zA-Z0-9-_]+)\.([a-zA-Z0-9-_]+)$/, {
|
|
481
|
+
message: "Not a valid compact jwt"
|
|
482
|
+
});
|
|
483
|
+
var zJwtConfirmationPayload = import_zod3.default.looseObject({
|
|
484
|
+
// RFC9449. jwk thumbprint of the dpop public key to which the access token is bound
|
|
485
|
+
jkt: import_zod3.default.string().optional(),
|
|
486
|
+
jwk: zJwk.optional()
|
|
487
|
+
});
|
|
488
|
+
var zJwtPayload = import_zod3.default.looseObject({
|
|
489
|
+
aud: import_zod3.default.string().or(import_zod3.default.array(import_zod3.default.string())).optional(),
|
|
490
|
+
cnf: zJwtConfirmationPayload.optional(),
|
|
491
|
+
exp: import_zod3.default.number().int().optional(),
|
|
492
|
+
iat: import_zod3.default.number().int().optional(),
|
|
493
|
+
iss: import_zod3.default.string().optional(),
|
|
494
|
+
jti: import_zod3.default.string().max(MAX_JTI_LENGTH).optional(),
|
|
495
|
+
nbf: import_zod3.default.number().int().optional(),
|
|
496
|
+
nonce: import_zod3.default.string().optional(),
|
|
497
|
+
// Reserved for status parameters
|
|
498
|
+
status: import_zod3.default.record(import_zod3.default.string(), import_zod3.default.any()).optional(),
|
|
499
|
+
sub: import_zod3.default.string().optional(),
|
|
500
|
+
// Reserved for OpenID Federation
|
|
501
|
+
trust_chain: zTrustChain.optional()
|
|
502
|
+
});
|
|
503
|
+
var zJwtHeader = import_zod3.default.looseObject({
|
|
504
|
+
alg: zAlgValueNotNone,
|
|
505
|
+
jwk: zJwk.optional(),
|
|
506
|
+
kid: import_zod3.default.string().optional(),
|
|
507
|
+
// Reserved for OpenID Federation
|
|
508
|
+
trust_chain: zTrustChain.optional(),
|
|
509
|
+
typ: import_zod3.default.string().optional(),
|
|
510
|
+
x5c: zCertificateChain.optional()
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// src/crypto/jwt/decode-jwt-header.ts
|
|
514
|
+
function decodeJwtHeader(options) {
|
|
515
|
+
const jwtParts = options.jwt.split(".");
|
|
516
|
+
if (jwtParts.length <= 2) {
|
|
517
|
+
throw new JwtParseError(
|
|
518
|
+
formatError(
|
|
519
|
+
"Unable to decode because Jwt is not a valid!",
|
|
520
|
+
options.errorMessagePrefix
|
|
521
|
+
)
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
const [headerPart] = jwtParts;
|
|
525
|
+
let headerJson;
|
|
526
|
+
try {
|
|
527
|
+
headerJson = stringToJsonWithErrorHandling(
|
|
528
|
+
encodeToUtf8String(decodeBase64(headerPart)),
|
|
529
|
+
formatError(
|
|
530
|
+
"Unable to parse jwt header to JSON",
|
|
531
|
+
options.errorMessagePrefix
|
|
532
|
+
)
|
|
533
|
+
);
|
|
534
|
+
} catch (error) {
|
|
535
|
+
throw new JwtParseError(
|
|
536
|
+
formatError(
|
|
537
|
+
`Error parsing JWT. ${error instanceof Error ? error.message : ""}`,
|
|
538
|
+
options.errorMessagePrefix
|
|
539
|
+
)
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
const header = parseWithErrorHandling(
|
|
543
|
+
options.headerSchema ?? zJwtHeader,
|
|
544
|
+
headerJson,
|
|
545
|
+
formatError("Invalid JWT header", options.errorMessagePrefix)
|
|
546
|
+
);
|
|
547
|
+
return {
|
|
548
|
+
header
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function jwtHeaderFromJwtSigner(signer) {
|
|
552
|
+
if (signer.method === "did") {
|
|
553
|
+
return {
|
|
554
|
+
alg: signer.alg,
|
|
555
|
+
kid: signer.didUrl
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
if (signer.method === "federation") {
|
|
559
|
+
return {
|
|
560
|
+
alg: signer.alg,
|
|
561
|
+
kid: signer.kid,
|
|
562
|
+
trust_chain: signer.trustChain
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
if (signer.method === "jwk") {
|
|
566
|
+
return {
|
|
567
|
+
alg: signer.alg,
|
|
568
|
+
jwk: signer.publicJwk
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
if (signer.method === "x5c") {
|
|
572
|
+
return {
|
|
573
|
+
alg: signer.alg,
|
|
574
|
+
kid: signer.kid,
|
|
575
|
+
trust_chain: signer.trustChain,
|
|
576
|
+
x5c: signer.x5c
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
return { alg: signer.alg };
|
|
580
|
+
}
|
|
581
|
+
function jwtSignerFromJwt(options) {
|
|
582
|
+
const { allowedSignerMethods, header, payload } = options;
|
|
583
|
+
const found = [];
|
|
584
|
+
if (header.x5c) {
|
|
585
|
+
found.push({
|
|
586
|
+
method: "x5c",
|
|
587
|
+
signer: {
|
|
588
|
+
alg: header.alg,
|
|
589
|
+
kid: header.kid,
|
|
590
|
+
method: "x5c",
|
|
591
|
+
trustChain: header.trust_chain,
|
|
592
|
+
x5c: header.x5c
|
|
593
|
+
},
|
|
594
|
+
valid: true
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
if (header.trust_chain) {
|
|
598
|
+
if (!header.kid) {
|
|
599
|
+
found.push({
|
|
600
|
+
error: "When 'trust_chain' is used in jwt header, the 'kid' parameter is required.",
|
|
601
|
+
method: "federation",
|
|
602
|
+
valid: false
|
|
603
|
+
});
|
|
604
|
+
} else {
|
|
605
|
+
found.push({
|
|
606
|
+
method: "federation",
|
|
607
|
+
signer: {
|
|
608
|
+
alg: header.alg,
|
|
609
|
+
kid: header.kid,
|
|
610
|
+
method: "federation",
|
|
611
|
+
trustChain: header.trust_chain
|
|
612
|
+
},
|
|
613
|
+
valid: true
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (header.kid?.startsWith("did:") || payload.iss?.startsWith("did:")) {
|
|
618
|
+
if (payload.iss && header.kid?.startsWith("did:") && !header.kid.startsWith(payload.iss)) {
|
|
619
|
+
found.push({
|
|
620
|
+
error: "kid in header starts with did that is different from did value in 'iss'",
|
|
621
|
+
method: "did",
|
|
622
|
+
valid: false
|
|
623
|
+
});
|
|
624
|
+
} else if (!header.kid?.startsWith("did:") && !header.kid?.startsWith("#")) {
|
|
625
|
+
found.push({
|
|
626
|
+
error: "kid in header must start with either 'did:' or '#' when 'iss' value is a did",
|
|
627
|
+
method: "did",
|
|
628
|
+
valid: false
|
|
629
|
+
});
|
|
630
|
+
} else if (header.kid) {
|
|
631
|
+
found.push({
|
|
632
|
+
method: "did",
|
|
633
|
+
signer: {
|
|
634
|
+
alg: header.alg,
|
|
635
|
+
didUrl: header.kid.startsWith("did:") ? header.kid : `${payload.iss}${header.kid}`,
|
|
636
|
+
method: "did"
|
|
637
|
+
},
|
|
638
|
+
valid: true
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
if (header.jwk) {
|
|
643
|
+
found.push({
|
|
644
|
+
method: "jwk",
|
|
645
|
+
signer: {
|
|
646
|
+
alg: header.alg,
|
|
647
|
+
method: "jwk",
|
|
648
|
+
publicJwk: header.jwk
|
|
649
|
+
},
|
|
650
|
+
valid: true
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
const allowedFoundMethods = found.filter(
|
|
654
|
+
(candidate) => !allowedSignerMethods || allowedSignerMethods.includes(candidate.method)
|
|
655
|
+
);
|
|
656
|
+
const allowedValidMethods = allowedFoundMethods.filter(
|
|
657
|
+
(candidate) => candidate.valid
|
|
658
|
+
);
|
|
659
|
+
const firstAllowedValidMethod = allowedValidMethods[0];
|
|
660
|
+
if (firstAllowedValidMethod && firstAllowedValidMethod.valid) {
|
|
661
|
+
return firstAllowedValidMethod.signer;
|
|
662
|
+
}
|
|
663
|
+
if (allowedFoundMethods.length > 0) {
|
|
664
|
+
throw new JwtParseError(
|
|
665
|
+
`Unable to extract signer method from jwt. Found ${allowedFoundMethods.length} allowed signer method(s) but contained invalid configuration:
|
|
666
|
+
${allowedFoundMethods.map(
|
|
667
|
+
(candidate) => candidate.valid ? "" : `FAILED: method ${candidate.method} - ${candidate.error}`
|
|
668
|
+
).join("\n")}`
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
if (found.length > 0) {
|
|
672
|
+
throw new JwtParseError(
|
|
673
|
+
`Unable to extract signer method from jwt. Found ${found.length} signer method(s) that are not allowed:
|
|
674
|
+
${found.map(
|
|
675
|
+
(candidate) => candidate.valid ? `SUCCEEDED: method ${candidate.method}` : `FAILED: method ${candidate.method} - ${candidate.error}`
|
|
676
|
+
).join("\n")}`
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
if (!allowedSignerMethods || allowedSignerMethods.includes("custom")) {
|
|
680
|
+
return {
|
|
681
|
+
alg: header.alg,
|
|
682
|
+
kid: header.kid,
|
|
683
|
+
method: "custom"
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
throw new JwtParseError(
|
|
687
|
+
"Unable to extract signer method from jwt. Found no signer methods and 'custom' signer method is not allowed."
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// src/crypto/jwt/decode-jwt.ts
|
|
692
|
+
function decodeJwt(options) {
|
|
693
|
+
const jwtParts = options.jwt.split(".");
|
|
694
|
+
if (jwtParts.length !== 3) {
|
|
695
|
+
throw new JwtParseError(
|
|
696
|
+
formatError(
|
|
697
|
+
"Unable to decode because Jwt is not a valid!",
|
|
698
|
+
options.errorMessagePrefix
|
|
699
|
+
)
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
let payloadJson;
|
|
703
|
+
try {
|
|
704
|
+
const payloadPart = jwtParts[1];
|
|
705
|
+
if (payloadPart === void 0) {
|
|
706
|
+
throw new JwtParseError(
|
|
707
|
+
formatError(
|
|
708
|
+
"Unable to decode because Jwt is not a valid!",
|
|
709
|
+
options.errorMessagePrefix
|
|
710
|
+
)
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
payloadJson = stringToJsonWithErrorHandling(
|
|
714
|
+
encodeToUtf8String(decodeBase64(payloadPart)),
|
|
715
|
+
formatError(
|
|
716
|
+
"Unable to parse jwt payload to JSON",
|
|
717
|
+
options.errorMessagePrefix
|
|
718
|
+
)
|
|
719
|
+
);
|
|
720
|
+
} catch (error) {
|
|
721
|
+
throw new JwtParseError(
|
|
722
|
+
formatError(
|
|
723
|
+
`Error parsing JWT. ${error instanceof Error ? error.message : ""}`,
|
|
724
|
+
options.errorMessagePrefix
|
|
725
|
+
)
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
const signaturePart = jwtParts[2];
|
|
729
|
+
if (signaturePart === void 0) {
|
|
730
|
+
throw new JwtParseError(
|
|
731
|
+
formatError(
|
|
732
|
+
"Unable to decode because Jwt is not a valid!",
|
|
733
|
+
options.errorMessagePrefix
|
|
734
|
+
)
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
const { header } = decodeJwtHeader({
|
|
738
|
+
errorMessagePrefix: options.errorMessagePrefix,
|
|
739
|
+
headerSchema: options.headerSchema,
|
|
740
|
+
jwt: options.jwt
|
|
741
|
+
});
|
|
742
|
+
const payload = parseWithErrorHandling(
|
|
743
|
+
options.payloadSchema ?? zJwtPayload,
|
|
744
|
+
payloadJson,
|
|
745
|
+
formatError("Invalid JWT payload", options.errorMessagePrefix)
|
|
746
|
+
);
|
|
747
|
+
return {
|
|
748
|
+
compact: options.jwt,
|
|
749
|
+
header,
|
|
750
|
+
payload,
|
|
751
|
+
signature: signaturePart
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// src/date.ts
|
|
756
|
+
function dateToSeconds(date) {
|
|
757
|
+
const milliseconds = date?.getTime() ?? Date.now();
|
|
758
|
+
return Math.floor(milliseconds / 1e3);
|
|
759
|
+
}
|
|
760
|
+
function addSecondsToDate(date, seconds) {
|
|
761
|
+
return new Date(date.getTime() + seconds * 1e3);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// src/crypto/jwt/verify-jwt.ts
|
|
765
|
+
async function verifyJwt(options) {
|
|
766
|
+
const errorMessage = options.errorMessage ?? "Error during verification of jwt.";
|
|
767
|
+
let signerJwk;
|
|
768
|
+
try {
|
|
769
|
+
const result = await options.verifyJwtCallback(options.signer, {
|
|
770
|
+
compact: options.compact,
|
|
771
|
+
header: options.header,
|
|
772
|
+
payload: options.payload
|
|
773
|
+
});
|
|
774
|
+
if (!result.verified) {
|
|
775
|
+
throw new JwtVerificationError(errorMessage);
|
|
776
|
+
}
|
|
777
|
+
signerJwk = result.signerJwk;
|
|
778
|
+
} catch (error) {
|
|
779
|
+
if (error instanceof JwtVerificationError) {
|
|
780
|
+
throw error;
|
|
781
|
+
}
|
|
782
|
+
throw new JwtVerificationError(errorMessage, { cause: error });
|
|
783
|
+
}
|
|
784
|
+
const nowInSeconds = dateToSeconds(options.now ?? /* @__PURE__ */ new Date());
|
|
785
|
+
const skewInSeconds = options.allowedSkewInSeconds ?? 0;
|
|
786
|
+
const timeBasedValidation = options.skipTimeBasedValidation !== void 0 ? !options.skipTimeBasedValidation : true;
|
|
787
|
+
if (timeBasedValidation && options.payload.nbf && nowInSeconds < options.payload.nbf - skewInSeconds) {
|
|
788
|
+
throw new JwtVerificationError(
|
|
789
|
+
`${errorMessage} jwt 'nbf' is in the future`
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
if (timeBasedValidation && options.payload.exp && nowInSeconds > options.payload.exp + skewInSeconds) {
|
|
793
|
+
throw new JwtVerificationError(`${errorMessage} jwt 'exp' is in the past`);
|
|
794
|
+
}
|
|
795
|
+
if (options.expectedAudience) {
|
|
796
|
+
const { aud } = options.payload;
|
|
797
|
+
if (Array.isArray(aud) && !aud.includes(options.expectedAudience) || typeof aud === "string" && aud !== options.expectedAudience) {
|
|
798
|
+
throw new JwtVerificationError(
|
|
799
|
+
`${errorMessage} jwt 'aud' does not match expected value.`
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
if (options.expectedIssuer && options.expectedIssuer !== options.payload.iss) {
|
|
804
|
+
throw new JwtVerificationError(
|
|
805
|
+
`${errorMessage} jwt 'iss' does not match expected value.`
|
|
806
|
+
);
|
|
807
|
+
}
|
|
808
|
+
if (options.expectedNonce && options.expectedNonce !== options.payload.nonce) {
|
|
809
|
+
throw new JwtVerificationError(
|
|
810
|
+
`${errorMessage} jwt 'nonce' does not match expected value.`
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
if (options.expectedSubject && options.expectedSubject !== options.payload.sub) {
|
|
814
|
+
throw new JwtVerificationError(
|
|
815
|
+
`${errorMessage} jwt 'sub' does not match expected value.`
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
if (options.requiredClaims) {
|
|
819
|
+
for (const claim of options.requiredClaims) {
|
|
820
|
+
if (!options.payload[claim]) {
|
|
821
|
+
throw new JwtVerificationError(
|
|
822
|
+
`${errorMessage} jwt '${claim}' is missing.`
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return {
|
|
828
|
+
signer: {
|
|
829
|
+
...options.signer,
|
|
830
|
+
publicJwk: signerJwk
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// src/crypto/jwt/z-jwe.ts
|
|
836
|
+
var import_zod4 = require("zod");
|
|
837
|
+
var zCompactJwe = import_zod4.z.string().regex(
|
|
838
|
+
/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/,
|
|
839
|
+
{
|
|
840
|
+
message: "Not a valid compact jwe"
|
|
841
|
+
}
|
|
842
|
+
);
|
|
843
|
+
|
|
844
|
+
// src/fetcher.ts
|
|
845
|
+
var defaultFetcher = fetch;
|
|
846
|
+
function createFetcher(fetcher = defaultFetcher) {
|
|
847
|
+
return (input, init) => fetcher(
|
|
848
|
+
input,
|
|
849
|
+
init ? {
|
|
850
|
+
...init,
|
|
851
|
+
body: init.body instanceof URLSearchParams ? init.body.toString() : init.body
|
|
852
|
+
} : void 0
|
|
853
|
+
).catch((error) => {
|
|
854
|
+
throw new Error(
|
|
855
|
+
`Unknown error occurred during fetch to '${String(input)}'
|
|
856
|
+
Cause: ${error instanceof Error ? error.message : String(error)}`
|
|
857
|
+
);
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
var hasStatusOrThrow = (status, customError) => async (res) => {
|
|
861
|
+
if (Array.isArray(status) ? !status.includes(res.status) : res.status !== status) {
|
|
862
|
+
const ErrorClass = customError ?? UnexpectedStatusCodeError;
|
|
863
|
+
throw new ErrorClass({
|
|
864
|
+
message: `Http request failed. Expected ${status}, got ${res.status}, url: ${res.url}`,
|
|
865
|
+
reason: await parseRawHttpResponse(res),
|
|
866
|
+
// Pass the response body as reason so the original error can surface
|
|
867
|
+
statusCode: res.status
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
return res;
|
|
871
|
+
};
|
|
872
|
+
var parseRawHttpResponse = (response) => response.headers.get("content-type")?.includes("application/json") ? response.json() : response.text();
|
|
873
|
+
|
|
874
|
+
// src/url.ts
|
|
875
|
+
function objectToQueryParams(object) {
|
|
876
|
+
const params = new URLSearchParams();
|
|
877
|
+
for (const [key, value] of Object.entries(object)) {
|
|
878
|
+
if (value != null) {
|
|
879
|
+
params.append(
|
|
880
|
+
key,
|
|
881
|
+
typeof value === "object" ? JSON.stringify(value) : String(value)
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
return params;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// src/validation.ts
|
|
889
|
+
var import_zod5 = __toESM(require("zod"));
|
|
890
|
+
var zItwSupportedSignatureAlg = import_zod5.default.enum([
|
|
283
891
|
"ES256",
|
|
284
892
|
"ES384",
|
|
285
893
|
"ES512",
|
|
@@ -287,7 +895,7 @@ var zItwSupportedSignatureAlg = import_zod.default.enum([
|
|
|
287
895
|
"PS384",
|
|
288
896
|
"PS512"
|
|
289
897
|
]);
|
|
290
|
-
var zHttpMethod =
|
|
898
|
+
var zHttpMethod = import_zod5.default.enum([
|
|
291
899
|
"GET",
|
|
292
900
|
"POST",
|
|
293
901
|
"PUT",
|
|
@@ -337,10 +945,6 @@ function dispatchByVersion(version, handlers) {
|
|
|
337
945
|
}
|
|
338
946
|
return handler();
|
|
339
947
|
}
|
|
340
|
-
|
|
341
|
-
// src/index.ts
|
|
342
|
-
var import_oauth2 = require("@openid4vc/oauth2");
|
|
343
|
-
var import_utils = require("@openid4vc/utils");
|
|
344
948
|
// Annotate the CommonJS export names for ESM import in node:
|
|
345
949
|
0 && (module.exports = {
|
|
346
950
|
CLOCK_SKEW_TOLERANCE_SECONDS,
|
|
@@ -352,7 +956,10 @@ var import_utils = require("@openid4vc/utils");
|
|
|
352
956
|
ItWalletSpecsVersion,
|
|
353
957
|
ItWalletSpecsVersionError,
|
|
354
958
|
JsonParseError,
|
|
959
|
+
JwtParseError,
|
|
960
|
+
JwtVerificationError,
|
|
355
961
|
MAX_IAT_AGE_SECONDS,
|
|
962
|
+
MAX_JTI_LENGTH,
|
|
356
963
|
UnexpectedStatusCodeError,
|
|
357
964
|
ValidationError,
|
|
358
965
|
addSecondsToDate,
|
|
@@ -362,6 +969,7 @@ var import_utils = require("@openid4vc/utils");
|
|
|
362
969
|
dateToSeconds,
|
|
363
970
|
decodeBase64,
|
|
364
971
|
decodeJwt,
|
|
972
|
+
decodeJwtHeader,
|
|
365
973
|
decodeUtf8String,
|
|
366
974
|
dispatchByVersion,
|
|
367
975
|
encodeToBase64Url,
|
|
@@ -370,15 +978,27 @@ var import_utils = require("@openid4vc/utils");
|
|
|
370
978
|
formatZodError,
|
|
371
979
|
hasConfigVersion,
|
|
372
980
|
hasStatusOrThrow,
|
|
981
|
+
jwtHeaderFromJwtSigner,
|
|
982
|
+
jwtSignerFromJwt,
|
|
373
983
|
objectToQueryParams,
|
|
374
984
|
parseIfJson,
|
|
375
985
|
parseRawHttpResponse,
|
|
376
986
|
parseWithErrorHandling,
|
|
377
987
|
serializeAttrs,
|
|
378
|
-
setGlobalConfig,
|
|
379
988
|
stringToJsonWithErrorHandling,
|
|
989
|
+
verifyJwt,
|
|
380
990
|
verifyJwtIatOrThrow,
|
|
991
|
+
zAlgValueNotNone,
|
|
992
|
+
zCertificateChain,
|
|
993
|
+
zCompactJwe,
|
|
994
|
+
zCompactJwt,
|
|
381
995
|
zHttpMethod,
|
|
382
|
-
zItwSupportedSignatureAlg
|
|
996
|
+
zItwSupportedSignatureAlg,
|
|
997
|
+
zJwk,
|
|
998
|
+
zJwkSet,
|
|
999
|
+
zJwtConfirmationPayload,
|
|
1000
|
+
zJwtHeader,
|
|
1001
|
+
zJwtPayload,
|
|
1002
|
+
zTrustChain
|
|
383
1003
|
});
|
|
384
1004
|
//# sourceMappingURL=index.js.map
|