@openid4vc/oauth2 0.3.1-alpha-20251124151046 → 0.4.0-alpha-20251127093634
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 +2 -2
- package/package.json +4 -5
- package/dist/index.cjs +0 -3093
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -3705
package/dist/index.cjs
DELETED
|
@@ -1,3093 +0,0 @@
|
|
|
1
|
-
//#region rolldown:runtime
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
-
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
-
key = keys[i];
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
13
|
-
__defProp(to, key, {
|
|
14
|
-
get: ((k) => from[k]).bind(null, key),
|
|
15
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return to;
|
|
21
|
-
};
|
|
22
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
-
value: mod,
|
|
24
|
-
enumerable: true
|
|
25
|
-
}) : target, mod));
|
|
26
|
-
|
|
27
|
-
//#endregion
|
|
28
|
-
let __openid4vc_utils = require("@openid4vc/utils");
|
|
29
|
-
let zod = require("zod");
|
|
30
|
-
zod = __toESM(zod);
|
|
31
|
-
|
|
32
|
-
//#region src/callbacks.ts
|
|
33
|
-
/**
|
|
34
|
-
* Supported hashing algorithms
|
|
35
|
-
*
|
|
36
|
-
* Based on https://www.iana.org/assignments/named-information/named-information.xhtml
|
|
37
|
-
*/
|
|
38
|
-
let HashAlgorithm = /* @__PURE__ */ function(HashAlgorithm$1) {
|
|
39
|
-
HashAlgorithm$1["Sha256"] = "sha-256";
|
|
40
|
-
HashAlgorithm$1["Sha384"] = "sha-384";
|
|
41
|
-
HashAlgorithm$1["Sha512"] = "sha-512";
|
|
42
|
-
return HashAlgorithm$1;
|
|
43
|
-
}({});
|
|
44
|
-
|
|
45
|
-
//#endregion
|
|
46
|
-
//#region src/error/Oauth2Error.ts
|
|
47
|
-
var Oauth2Error = class extends Error {
|
|
48
|
-
constructor(message, options) {
|
|
49
|
-
const errorMessage = message ?? "Unknown error occurred.";
|
|
50
|
-
const causeMessage = options?.cause instanceof Error ? ` ${options.cause.message}` : options?.cause ? ` ${options?.cause}` : "";
|
|
51
|
-
super(`${errorMessage}${causeMessage}`);
|
|
52
|
-
this.cause = options?.cause;
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
//#endregion
|
|
57
|
-
//#region src/common/jwk/jwk-thumbprint.ts
|
|
58
|
-
const zJwkThumbprintComponents = zod.default.discriminatedUnion("kty", [
|
|
59
|
-
zod.default.object({
|
|
60
|
-
kty: zod.default.literal("EC"),
|
|
61
|
-
crv: zod.default.string(),
|
|
62
|
-
x: zod.default.string(),
|
|
63
|
-
y: zod.default.string()
|
|
64
|
-
}),
|
|
65
|
-
zod.default.object({
|
|
66
|
-
kty: zod.default.literal("OKP"),
|
|
67
|
-
crv: zod.default.string(),
|
|
68
|
-
x: zod.default.string()
|
|
69
|
-
}),
|
|
70
|
-
zod.default.object({
|
|
71
|
-
kty: zod.default.literal("RSA"),
|
|
72
|
-
e: zod.default.string(),
|
|
73
|
-
n: zod.default.string()
|
|
74
|
-
}),
|
|
75
|
-
zod.default.object({
|
|
76
|
-
kty: zod.default.literal("oct"),
|
|
77
|
-
k: zod.default.string()
|
|
78
|
-
})
|
|
79
|
-
]).transform((data) => {
|
|
80
|
-
if (data.kty === "EC") return {
|
|
81
|
-
crv: data.crv,
|
|
82
|
-
kty: data.kty,
|
|
83
|
-
x: data.x,
|
|
84
|
-
y: data.y
|
|
85
|
-
};
|
|
86
|
-
if (data.kty === "OKP") return {
|
|
87
|
-
crv: data.crv,
|
|
88
|
-
kty: data.kty,
|
|
89
|
-
x: data.x
|
|
90
|
-
};
|
|
91
|
-
if (data.kty === "RSA") return {
|
|
92
|
-
e: data.e,
|
|
93
|
-
kty: data.kty,
|
|
94
|
-
n: data.n
|
|
95
|
-
};
|
|
96
|
-
if (data.kty === "oct") return {
|
|
97
|
-
k: data.k,
|
|
98
|
-
kty: data.kty
|
|
99
|
-
};
|
|
100
|
-
throw new Error("Unsupported kty");
|
|
101
|
-
});
|
|
102
|
-
async function calculateJwkThumbprint(options) {
|
|
103
|
-
const jwkThumbprintComponents = (0, __openid4vc_utils.parseWithErrorHandling)(zJwkThumbprintComponents, options.jwk, `Provided jwk does not match a supported jwk structure. Either the 'kty' is not supported, or required values are missing.`);
|
|
104
|
-
return (0, __openid4vc_utils.encodeToBase64Url)(await options.hashCallback((0, __openid4vc_utils.decodeUtf8String)(JSON.stringify(jwkThumbprintComponents)), options.hashAlgorithm));
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
//#endregion
|
|
108
|
-
//#region src/common/jwk/jwks.ts
|
|
109
|
-
/**
|
|
110
|
-
*
|
|
111
|
-
* @param header
|
|
112
|
-
* @param jwks
|
|
113
|
-
*/
|
|
114
|
-
function extractJwkFromJwksForJwt(options) {
|
|
115
|
-
const jwksForUse = options.jwks.keys.filter(({ use }) => !use || use === options.use);
|
|
116
|
-
const jwkForKid = options.kid ? jwksForUse.find(({ kid }) => kid === options.kid) : void 0;
|
|
117
|
-
if (jwkForKid) return jwkForKid;
|
|
118
|
-
if (jwksForUse.length === 1) return jwksForUse[0];
|
|
119
|
-
throw new Oauth2Error(`Unable to extract jwk from jwks for use '${options.use}'${options.kid ? `with kid '${options.kid}'.` : ". No kid provided and more than jwk."}`);
|
|
120
|
-
}
|
|
121
|
-
async function isJwkInSet({ jwk, jwks, callbacks }) {
|
|
122
|
-
const jwkThumbprint = await calculateJwkThumbprint({
|
|
123
|
-
hashAlgorithm: HashAlgorithm.Sha256,
|
|
124
|
-
hashCallback: callbacks.hash,
|
|
125
|
-
jwk
|
|
126
|
-
});
|
|
127
|
-
for (const jwkFromSet of jwks) if (await calculateJwkThumbprint({
|
|
128
|
-
hashAlgorithm: HashAlgorithm.Sha256,
|
|
129
|
-
hashCallback: callbacks.hash,
|
|
130
|
-
jwk: jwkFromSet
|
|
131
|
-
}) === jwkThumbprint) return true;
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
//#endregion
|
|
136
|
-
//#region src/error/Oauth2JwtParseError.ts
|
|
137
|
-
var Oauth2JwtParseError = class extends Oauth2Error {
|
|
138
|
-
constructor(message) {
|
|
139
|
-
super(message ?? "Error parsing jwt");
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
//#endregion
|
|
144
|
-
//#region src/common/jwk/z-jwk.ts
|
|
145
|
-
const zJwk = zod.default.object({
|
|
146
|
-
kty: zod.default.string(),
|
|
147
|
-
crv: zod.default.optional(zod.default.string()),
|
|
148
|
-
x: zod.default.optional(zod.default.string()),
|
|
149
|
-
y: zod.default.optional(zod.default.string()),
|
|
150
|
-
e: zod.default.optional(zod.default.string()),
|
|
151
|
-
n: zod.default.optional(zod.default.string()),
|
|
152
|
-
alg: zod.default.optional(zod.default.string()),
|
|
153
|
-
d: zod.default.optional(zod.default.string()),
|
|
154
|
-
dp: zod.default.optional(zod.default.string()),
|
|
155
|
-
dq: zod.default.optional(zod.default.string()),
|
|
156
|
-
ext: zod.default.optional(zod.default.boolean()),
|
|
157
|
-
k: zod.default.optional(zod.default.string()),
|
|
158
|
-
key_ops: zod.default.optional(zod.default.array(zod.default.string())),
|
|
159
|
-
kid: zod.default.optional(zod.default.string()),
|
|
160
|
-
oth: zod.default.optional(zod.default.array(zod.default.object({
|
|
161
|
-
d: zod.default.optional(zod.default.string()),
|
|
162
|
-
r: zod.default.optional(zod.default.string()),
|
|
163
|
-
t: zod.default.optional(zod.default.string())
|
|
164
|
-
}).loose())),
|
|
165
|
-
p: zod.default.optional(zod.default.string()),
|
|
166
|
-
q: zod.default.optional(zod.default.string()),
|
|
167
|
-
qi: zod.default.optional(zod.default.string()),
|
|
168
|
-
use: zod.default.optional(zod.default.string()),
|
|
169
|
-
x5c: zod.default.optional(zod.default.array(zod.default.string())),
|
|
170
|
-
x5t: zod.default.optional(zod.default.string()),
|
|
171
|
-
"x5t#S256": zod.default.optional(zod.default.string()),
|
|
172
|
-
x5u: zod.default.optional(zod.default.string())
|
|
173
|
-
}).loose();
|
|
174
|
-
const zJwkSet = zod.default.object({ keys: zod.default.array(zJwk) }).loose();
|
|
175
|
-
|
|
176
|
-
//#endregion
|
|
177
|
-
//#region src/common/z-common.ts
|
|
178
|
-
const zAlgValueNotNone = zod.default.string().refine((alg) => alg !== "none", { message: `alg value may not be 'none'` });
|
|
179
|
-
|
|
180
|
-
//#endregion
|
|
181
|
-
//#region src/common/jwt/z-jwt.ts
|
|
182
|
-
const zCompactJwt = zod.default.string().regex(/^([a-zA-Z0-9-_]+)\.([a-zA-Z0-9-_]+)\.([a-zA-Z0-9-_]+)$/, { message: "Not a valid compact jwt" });
|
|
183
|
-
const zJwtConfirmationPayload = zod.default.object({
|
|
184
|
-
jwk: zJwk.optional(),
|
|
185
|
-
jkt: zod.default.string().optional()
|
|
186
|
-
}).loose();
|
|
187
|
-
const zJwtPayload = zod.default.object({
|
|
188
|
-
iss: zod.default.string().optional(),
|
|
189
|
-
aud: zod.default.union([zod.default.string(), zod.default.array(zod.default.string())]).optional(),
|
|
190
|
-
iat: __openid4vc_utils.zInteger.optional(),
|
|
191
|
-
exp: __openid4vc_utils.zInteger.optional(),
|
|
192
|
-
nbf: __openid4vc_utils.zInteger.optional(),
|
|
193
|
-
nonce: zod.default.string().optional(),
|
|
194
|
-
jti: zod.default.string().optional(),
|
|
195
|
-
sub: zod.default.string().optional(),
|
|
196
|
-
cnf: zJwtConfirmationPayload.optional(),
|
|
197
|
-
status: zod.default.record(zod.default.string(), zod.default.any()).optional(),
|
|
198
|
-
trust_chain: zod.default.tuple([zod.default.string()], zod.default.string()).optional()
|
|
199
|
-
}).loose();
|
|
200
|
-
const zJwtHeader = zod.default.object({
|
|
201
|
-
alg: zAlgValueNotNone,
|
|
202
|
-
typ: zod.default.string().optional(),
|
|
203
|
-
kid: zod.default.string().optional(),
|
|
204
|
-
jwk: zJwk.optional(),
|
|
205
|
-
x5c: zod.default.array(zod.default.string()).optional(),
|
|
206
|
-
trust_chain: zod.default.tuple([zod.default.string()], zod.default.string()).optional()
|
|
207
|
-
}).loose();
|
|
208
|
-
|
|
209
|
-
//#endregion
|
|
210
|
-
//#region src/common/jwt/decode-jwt-header.ts
|
|
211
|
-
function decodeJwtHeader(options) {
|
|
212
|
-
const jwtParts = options.jwt.split(".");
|
|
213
|
-
if (jwtParts.length <= 2) throw new Oauth2JwtParseError("Jwt is not a valid jwt, unable to decode");
|
|
214
|
-
let headerJson;
|
|
215
|
-
try {
|
|
216
|
-
headerJson = (0, __openid4vc_utils.stringToJsonWithErrorHandling)((0, __openid4vc_utils.encodeToUtf8String)((0, __openid4vc_utils.decodeBase64)(jwtParts[0])), "Unable to parse jwt header to JSON");
|
|
217
|
-
} catch (error) {
|
|
218
|
-
throw new Oauth2JwtParseError(`Error parsing JWT. ${error instanceof Error ? error.message : ""}`);
|
|
219
|
-
}
|
|
220
|
-
return { header: (0, __openid4vc_utils.parseWithErrorHandling)(options.headerSchema ?? zJwtHeader, headerJson) };
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
//#endregion
|
|
224
|
-
//#region src/common/jwt/decode-jwt.ts
|
|
225
|
-
function decodeJwt(options) {
|
|
226
|
-
const jwtParts = options.jwt.split(".");
|
|
227
|
-
if (jwtParts.length !== 3) throw new Oauth2JwtParseError("Jwt is not a valid jwt, unable to decode");
|
|
228
|
-
let payloadJson;
|
|
229
|
-
try {
|
|
230
|
-
payloadJson = (0, __openid4vc_utils.stringToJsonWithErrorHandling)((0, __openid4vc_utils.encodeToUtf8String)((0, __openid4vc_utils.decodeBase64)(jwtParts[1])), "Unable to parse jwt payload to JSON");
|
|
231
|
-
} catch (error) {
|
|
232
|
-
throw new Oauth2JwtParseError(`Error parsing JWT. ${error instanceof Error ? error.message : ""}`);
|
|
233
|
-
}
|
|
234
|
-
const { header } = decodeJwtHeader({
|
|
235
|
-
jwt: options.jwt,
|
|
236
|
-
headerSchema: options.headerSchema
|
|
237
|
-
});
|
|
238
|
-
return {
|
|
239
|
-
header,
|
|
240
|
-
payload: (0, __openid4vc_utils.parseWithErrorHandling)(options.payloadSchema ?? zJwtPayload, payloadJson),
|
|
241
|
-
signature: jwtParts[2],
|
|
242
|
-
compact: options.jwt
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
function jwtHeaderFromJwtSigner(signer) {
|
|
246
|
-
if (signer.method === "did") return {
|
|
247
|
-
alg: signer.alg,
|
|
248
|
-
kid: signer.didUrl
|
|
249
|
-
};
|
|
250
|
-
if (signer.method === "federation") return {
|
|
251
|
-
alg: signer.alg,
|
|
252
|
-
kid: signer.kid,
|
|
253
|
-
trust_chain: signer.trustChain
|
|
254
|
-
};
|
|
255
|
-
if (signer.method === "jwk") return {
|
|
256
|
-
alg: signer.alg,
|
|
257
|
-
jwk: signer.publicJwk
|
|
258
|
-
};
|
|
259
|
-
if (signer.method === "x5c") return {
|
|
260
|
-
alg: signer.alg,
|
|
261
|
-
x5c: signer.x5c
|
|
262
|
-
};
|
|
263
|
-
return { alg: signer.alg };
|
|
264
|
-
}
|
|
265
|
-
function jwtSignerFromJwt({ header, payload, allowedSignerMethods }) {
|
|
266
|
-
const found = [];
|
|
267
|
-
if (header.x5c) found.push({
|
|
268
|
-
method: "x5c",
|
|
269
|
-
valid: true,
|
|
270
|
-
signer: {
|
|
271
|
-
alg: header.alg,
|
|
272
|
-
method: "x5c",
|
|
273
|
-
x5c: header.x5c,
|
|
274
|
-
kid: header.kid
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
if (header.trust_chain) if (!header.kid) found.push({
|
|
278
|
-
method: "federation",
|
|
279
|
-
valid: false,
|
|
280
|
-
error: `When 'trust_chain' is used in jwt header, the 'kid' parameter is required.`
|
|
281
|
-
});
|
|
282
|
-
else found.push({
|
|
283
|
-
method: "federation",
|
|
284
|
-
valid: true,
|
|
285
|
-
signer: {
|
|
286
|
-
alg: header.alg,
|
|
287
|
-
trustChain: header.trust_chain,
|
|
288
|
-
kid: header.kid,
|
|
289
|
-
method: "federation"
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
if (header.kid?.startsWith("did:") || payload.iss?.startsWith("did:")) if (payload.iss && header.kid?.startsWith("did:") && !header.kid.startsWith(payload.iss)) found.push({
|
|
293
|
-
method: "did",
|
|
294
|
-
valid: false,
|
|
295
|
-
error: `kid in header starts with did that is different from did value in 'iss'`
|
|
296
|
-
});
|
|
297
|
-
else if (!header.kid?.startsWith("did:") && !header.kid?.startsWith("#")) found.push({
|
|
298
|
-
method: "did",
|
|
299
|
-
valid: false,
|
|
300
|
-
error: `kid in header must start with either 'did:' or '#' when 'iss' value is a did`
|
|
301
|
-
});
|
|
302
|
-
else found.push({
|
|
303
|
-
method: "did",
|
|
304
|
-
valid: true,
|
|
305
|
-
signer: {
|
|
306
|
-
method: "did",
|
|
307
|
-
alg: header.alg,
|
|
308
|
-
didUrl: header.kid.startsWith("did:") ? header.kid : `${payload.iss}${header.kid}`
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
|
-
if (header.jwk) found.push({
|
|
312
|
-
method: "jwk",
|
|
313
|
-
signer: {
|
|
314
|
-
alg: header.alg,
|
|
315
|
-
method: "jwk",
|
|
316
|
-
publicJwk: header.jwk
|
|
317
|
-
},
|
|
318
|
-
valid: true
|
|
319
|
-
});
|
|
320
|
-
const allowedFoundMethods = found.filter((f) => !allowedSignerMethods || allowedSignerMethods?.includes(f.method));
|
|
321
|
-
const allowedValidMethods = allowedFoundMethods.filter((f) => f.valid);
|
|
322
|
-
if (allowedValidMethods.length > 0) return allowedValidMethods[0].signer;
|
|
323
|
-
if (allowedFoundMethods.length > 0) throw new Oauth2Error(`Unable to extract signer method from jwt. Found ${allowedFoundMethods.length} allowed signer method(s) but contained invalid configuration:\n${allowedFoundMethods.map((m) => m.valid ? "" : `FAILED: method ${m.method} - ${m.error}`).join("\n")}`);
|
|
324
|
-
if (found.length > 0) throw new Oauth2Error(`Unable to extract signer method from jwt. Found ${found.length} signer method(s) that are not allowed:\n${found.map((m) => m.valid ? `SUCCEEDED: method ${m.method}` : `FAILED: method ${m.method} - ${m.error}`).join("\n")}`);
|
|
325
|
-
if (!allowedSignerMethods || allowedSignerMethods.includes("custom")) return {
|
|
326
|
-
method: "custom",
|
|
327
|
-
alg: header.alg,
|
|
328
|
-
kid: header.kid
|
|
329
|
-
};
|
|
330
|
-
throw new Oauth2Error(`Unable to extract signer method from jwt. Found no signer methods and 'custom' signer method is not allowed.`);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
//#endregion
|
|
334
|
-
//#region src/error/Oauth2JwtVerificationError.ts
|
|
335
|
-
var Oauth2JwtVerificationError = class extends Oauth2Error {
|
|
336
|
-
constructor(message, options) {
|
|
337
|
-
super(message ?? "Error verifiying jwt.", options);
|
|
338
|
-
}
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
//#endregion
|
|
342
|
-
//#region src/common/jwt/verify-jwt.ts
|
|
343
|
-
async function verifyJwt(options) {
|
|
344
|
-
const errorMessage = options.errorMessage ?? "Error during verification of jwt.";
|
|
345
|
-
let signerJwk;
|
|
346
|
-
try {
|
|
347
|
-
const result = await options.verifyJwtCallback(options.signer, {
|
|
348
|
-
header: options.header,
|
|
349
|
-
payload: options.payload,
|
|
350
|
-
compact: options.compact
|
|
351
|
-
});
|
|
352
|
-
if (!result.verified) throw new Oauth2JwtVerificationError(errorMessage);
|
|
353
|
-
signerJwk = result.signerJwk;
|
|
354
|
-
} catch (error) {
|
|
355
|
-
if (error instanceof Oauth2JwtVerificationError) throw error;
|
|
356
|
-
throw new Oauth2JwtVerificationError(errorMessage, { cause: error });
|
|
357
|
-
}
|
|
358
|
-
const nowInSeconds = (0, __openid4vc_utils.dateToSeconds)(options.now ?? /* @__PURE__ */ new Date());
|
|
359
|
-
const skewInSeconds = options.allowedSkewInSeconds ?? 0;
|
|
360
|
-
const timeBasedValidation = options.skipTimeBasedValidation !== void 0 ? !options.skipTimeBasedValidation : true;
|
|
361
|
-
if (timeBasedValidation && options.payload.nbf && nowInSeconds < options.payload.nbf - skewInSeconds) throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'nbf' is in the future`);
|
|
362
|
-
if (timeBasedValidation && options.payload.exp && nowInSeconds > options.payload.exp + skewInSeconds) throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'exp' is in the past`);
|
|
363
|
-
if (options.expectedAudience) {
|
|
364
|
-
if (Array.isArray(options.payload.aud) && !options.payload.aud.includes(options.expectedAudience) || typeof options.payload.aud === "string" && options.payload.aud !== options.expectedAudience) throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'aud' does not match expected value.`);
|
|
365
|
-
}
|
|
366
|
-
if (options.expectedIssuer && options.expectedIssuer !== options.payload.iss) throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'iss' does not match expected value.`);
|
|
367
|
-
if (options.expectedNonce && options.expectedNonce !== options.payload.nonce) throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'nonce' does not match expected value.`);
|
|
368
|
-
if (options.expectedSubject && options.expectedSubject !== options.payload.sub) throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'sub' does not match expected value.`);
|
|
369
|
-
if (options.requiredClaims) {
|
|
370
|
-
for (const claim of options.requiredClaims) if (!options.payload[claim]) throw new Oauth2JwtVerificationError(`${errorMessage} jwt '${claim}' is missing.`);
|
|
371
|
-
}
|
|
372
|
-
return { signer: {
|
|
373
|
-
...options.signer,
|
|
374
|
-
publicJwk: signerJwk
|
|
375
|
-
} };
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
//#endregion
|
|
379
|
-
//#region ../utils/src/error/ValidationError.ts
|
|
380
|
-
var ValidationError$1 = class extends Error {
|
|
381
|
-
constructor(message, zodError) {
|
|
382
|
-
super(message);
|
|
383
|
-
this.message = `${message}\n${zodError ? zod.default.prettifyError(zodError) : ""}`;
|
|
384
|
-
Object.defineProperty(this, "zodError", {
|
|
385
|
-
value: zodError,
|
|
386
|
-
writable: false,
|
|
387
|
-
enumerable: false
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
//#endregion
|
|
393
|
-
//#region src/metadata/fetch-jwks-uri.ts
|
|
394
|
-
/**
|
|
395
|
-
* Fetch JWKs from a provided JWKs URI.
|
|
396
|
-
*
|
|
397
|
-
* Returns validated metadata if successful response
|
|
398
|
-
* Throws error otherwise
|
|
399
|
-
*
|
|
400
|
-
* @throws {ValidationError} if successful response but validation of response failed
|
|
401
|
-
* @throws {InvalidFetchResponseError} if unsuccesful response
|
|
402
|
-
*/
|
|
403
|
-
async function fetchJwks(jwksUrl, fetch) {
|
|
404
|
-
const { result, response } = await (0, __openid4vc_utils.createZodFetcher)(fetch)(zJwkSet, [__openid4vc_utils.ContentType.JwkSet, __openid4vc_utils.ContentType.Json], jwksUrl);
|
|
405
|
-
if (!response.ok) throw new __openid4vc_utils.InvalidFetchResponseError(`Fetching JWKs from jwks_uri '${jwksUrl}' resulted in an unsuccessful response with status code '${response.status}'.`, await response.clone().text(), response);
|
|
406
|
-
if (!result?.success) throw new ValidationError$1(`Validation of JWKs from jwks_uri '${jwksUrl}' failed`, result?.error);
|
|
407
|
-
return result.data;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
//#endregion
|
|
411
|
-
//#region src/access-token/z-access-token-jwt.ts
|
|
412
|
-
const zAccessTokenProfileJwtHeader = zod.default.object({
|
|
413
|
-
...zJwtHeader.shape,
|
|
414
|
-
typ: zod.default.enum(["application/at+jwt", "at+jwt"])
|
|
415
|
-
}).loose();
|
|
416
|
-
const zAccessTokenProfileJwtPayload = zod.default.object({
|
|
417
|
-
...zJwtPayload.shape,
|
|
418
|
-
iss: zod.default.string(),
|
|
419
|
-
exp: __openid4vc_utils.zInteger,
|
|
420
|
-
iat: __openid4vc_utils.zInteger,
|
|
421
|
-
aud: zod.default.union([zod.default.string(), zod.default.array(zod.default.string())]),
|
|
422
|
-
sub: zod.default.string(),
|
|
423
|
-
client_id: zod.default.optional(zod.default.string()),
|
|
424
|
-
jti: zod.default.string(),
|
|
425
|
-
scope: zod.default.optional(zod.default.string())
|
|
426
|
-
}).loose();
|
|
427
|
-
|
|
428
|
-
//#endregion
|
|
429
|
-
//#region src/access-token/verify-access-token.ts
|
|
430
|
-
let SupportedAuthenticationScheme = /* @__PURE__ */ function(SupportedAuthenticationScheme$1) {
|
|
431
|
-
SupportedAuthenticationScheme$1["Bearer"] = "Bearer";
|
|
432
|
-
SupportedAuthenticationScheme$1["DPoP"] = "DPoP";
|
|
433
|
-
return SupportedAuthenticationScheme$1;
|
|
434
|
-
}({});
|
|
435
|
-
/**
|
|
436
|
-
* Verify an access token as a JWT Profile access token.
|
|
437
|
-
*
|
|
438
|
-
* @throws {@link ValidationError} if the JWT header or payload does not align with JWT Profile rules
|
|
439
|
-
* @throws {@link Oauth2JwtParseError} if the jwt is not a valid jwt format, or the jwt header/payload cannot be parsed as JSON
|
|
440
|
-
* @throws {@link Oauth2JwtVerificationError} if the JWT verification fails (signature or nbf/exp)
|
|
441
|
-
* @throws {@link Oauth2JwtVerificationError} if the JWT verification fails (signature or nbf/exp)
|
|
442
|
-
*/
|
|
443
|
-
async function verifyJwtProfileAccessToken(options) {
|
|
444
|
-
const decodedJwt = decodeJwt({
|
|
445
|
-
jwt: options.accessToken,
|
|
446
|
-
headerSchema: zAccessTokenProfileJwtHeader,
|
|
447
|
-
payloadSchema: zAccessTokenProfileJwtPayload
|
|
448
|
-
});
|
|
449
|
-
const authorizationServer = options.authorizationServers.find(({ issuer }) => decodedJwt.payload.iss === issuer);
|
|
450
|
-
if (!authorizationServer) throw new Oauth2Error(`Access token jwt contains unrecognized authorization server 'iss' value of '${decodedJwt.payload.iss}'`);
|
|
451
|
-
const jwksUrl = authorizationServer.jwks_uri;
|
|
452
|
-
if (!jwksUrl) throw new Oauth2Error(`Authorization server '${authorizationServer.issuer}' does not have a 'jwks_uri' parameter to fetch JWKs.`);
|
|
453
|
-
const jwks = await fetchJwks(jwksUrl, options.callbacks.fetch);
|
|
454
|
-
const publicJwk = extractJwkFromJwksForJwt({
|
|
455
|
-
kid: decodedJwt.header.kid,
|
|
456
|
-
jwks,
|
|
457
|
-
use: "sig"
|
|
458
|
-
});
|
|
459
|
-
await verifyJwt({
|
|
460
|
-
compact: options.accessToken,
|
|
461
|
-
header: decodedJwt.header,
|
|
462
|
-
payload: decodedJwt.payload,
|
|
463
|
-
signer: {
|
|
464
|
-
method: "jwk",
|
|
465
|
-
publicJwk,
|
|
466
|
-
alg: decodedJwt.header.alg
|
|
467
|
-
},
|
|
468
|
-
verifyJwtCallback: options.callbacks.verifyJwt,
|
|
469
|
-
errorMessage: "Error during verification of access token jwt.",
|
|
470
|
-
now: options.now,
|
|
471
|
-
expectedAudience: options.resourceServer
|
|
472
|
-
});
|
|
473
|
-
return {
|
|
474
|
-
header: decodedJwt.header,
|
|
475
|
-
payload: decodedJwt.payload,
|
|
476
|
-
authorizationServer
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
//#endregion
|
|
481
|
-
//#region src/common/z-oauth2-error.ts
|
|
482
|
-
let Oauth2ErrorCodes = /* @__PURE__ */ function(Oauth2ErrorCodes$1) {
|
|
483
|
-
Oauth2ErrorCodes$1["ServerError"] = "server_error";
|
|
484
|
-
Oauth2ErrorCodes$1["InvalidTarget"] = "invalid_target";
|
|
485
|
-
Oauth2ErrorCodes$1["InvalidRequest"] = "invalid_request";
|
|
486
|
-
Oauth2ErrorCodes$1["InvalidToken"] = "invalid_token";
|
|
487
|
-
Oauth2ErrorCodes$1["InsufficientScope"] = "insufficient_scope";
|
|
488
|
-
Oauth2ErrorCodes$1["InvalidGrant"] = "invalid_grant";
|
|
489
|
-
Oauth2ErrorCodes$1["InvalidClient"] = "invalid_client";
|
|
490
|
-
Oauth2ErrorCodes$1["UnauthorizedClient"] = "unauthorized_client";
|
|
491
|
-
Oauth2ErrorCodes$1["UnsupportedGrantType"] = "unsupported_grant_type";
|
|
492
|
-
Oauth2ErrorCodes$1["InvalidScope"] = "invalid_scope";
|
|
493
|
-
Oauth2ErrorCodes$1["InvalidDpopProof"] = "invalid_dpop_proof";
|
|
494
|
-
Oauth2ErrorCodes$1["UseDpopNonce"] = "use_dpop_nonce";
|
|
495
|
-
Oauth2ErrorCodes$1["RedirectToWeb"] = "redirect_to_web";
|
|
496
|
-
Oauth2ErrorCodes$1["InvalidSession"] = "invalid_session";
|
|
497
|
-
Oauth2ErrorCodes$1["InsufficientAuthorization"] = "insufficient_authorization";
|
|
498
|
-
Oauth2ErrorCodes$1["InvalidCredentialRequest"] = "invalid_credential_request";
|
|
499
|
-
Oauth2ErrorCodes$1["CredentialRequestDenied"] = "credential_request_denied";
|
|
500
|
-
Oauth2ErrorCodes$1["InvalidProof"] = "invalid_proof";
|
|
501
|
-
Oauth2ErrorCodes$1["InvalidNonce"] = "invalid_nonce";
|
|
502
|
-
Oauth2ErrorCodes$1["InvalidEncryptionParameters"] = "invalid_encryption_parameters";
|
|
503
|
-
Oauth2ErrorCodes$1["UnknownCredentialConfiguration"] = "unknown_credential_configuration";
|
|
504
|
-
Oauth2ErrorCodes$1["UnknownCredentialIdentifier"] = "unknown_credential_identifier";
|
|
505
|
-
Oauth2ErrorCodes$1["InvalidTransactionId"] = "invalid_transaction_id";
|
|
506
|
-
Oauth2ErrorCodes$1["UnsupportedCredentialType"] = "unsupported_credential_type";
|
|
507
|
-
Oauth2ErrorCodes$1["UnsupportedCredentialFormat"] = "unsupported_credential_format";
|
|
508
|
-
Oauth2ErrorCodes$1["InvalidRequestUri"] = "invalid_request_uri";
|
|
509
|
-
Oauth2ErrorCodes$1["InvalidRequestObject"] = "invalid_request_object";
|
|
510
|
-
Oauth2ErrorCodes$1["RequestNotSupported"] = "request_not_supported";
|
|
511
|
-
Oauth2ErrorCodes$1["RequestUriNotSupported"] = "request_uri_not_supported";
|
|
512
|
-
Oauth2ErrorCodes$1["VpFormatsNotSupported"] = "vp_formats_not_supported";
|
|
513
|
-
Oauth2ErrorCodes$1["AccessDenied"] = "access_denied";
|
|
514
|
-
Oauth2ErrorCodes$1["InvalidPresentationDefinitionUri"] = "invalid_presentation_definition_uri";
|
|
515
|
-
Oauth2ErrorCodes$1["InvalidPresentationDefinitionReference"] = "invalid_presentation_definition_reference";
|
|
516
|
-
Oauth2ErrorCodes$1["InvalidRequestUriMethod"] = "invalid_request_uri_method";
|
|
517
|
-
Oauth2ErrorCodes$1["InvalidTransactionData"] = "invalid_transaction_data";
|
|
518
|
-
Oauth2ErrorCodes$1["WalletUnavailable"] = "wallet_unavailable";
|
|
519
|
-
return Oauth2ErrorCodes$1;
|
|
520
|
-
}({});
|
|
521
|
-
const zOauth2ErrorResponse = zod.default.object({
|
|
522
|
-
error: zod.default.union([zod.default.enum(Oauth2ErrorCodes), zod.default.string()]),
|
|
523
|
-
error_description: zod.default.string().optional(),
|
|
524
|
-
error_uri: zod.default.string().optional()
|
|
525
|
-
}).loose();
|
|
526
|
-
|
|
527
|
-
//#endregion
|
|
528
|
-
//#region src/error/Oauth2ServerErrorResponseError.ts
|
|
529
|
-
var Oauth2ServerErrorResponseError = class extends Oauth2Error {
|
|
530
|
-
constructor(errorResponse, options) {
|
|
531
|
-
super(`${options?.internalMessage ?? errorResponse.error_description}\n${JSON.stringify(errorResponse, null, 2)}`, options);
|
|
532
|
-
this.errorResponse = errorResponse;
|
|
533
|
-
this.status = options?.status ?? 400;
|
|
534
|
-
}
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
//#endregion
|
|
538
|
-
//#region src/common/jwt/z-jwe.ts
|
|
539
|
-
const zCompactJwe = zod.z.string().regex(/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/, { message: "Not a valid compact jwe" });
|
|
540
|
-
|
|
541
|
-
//#endregion
|
|
542
|
-
//#region src/jar/z-jar-authorization-request.ts
|
|
543
|
-
const zJarAuthorizationRequest = zod.z.object({
|
|
544
|
-
request: zod.z.optional(zod.z.string()),
|
|
545
|
-
request_uri: zod.z.optional(__openid4vc_utils.zHttpsUrl),
|
|
546
|
-
client_id: zod.z.optional(zod.z.string())
|
|
547
|
-
}).loose();
|
|
548
|
-
function validateJarRequestParams(options) {
|
|
549
|
-
const { jarRequestParams } = options;
|
|
550
|
-
if (jarRequestParams.request && jarRequestParams.request_uri) throw new Oauth2ServerErrorResponseError({
|
|
551
|
-
error: Oauth2ErrorCodes.InvalidRequestObject,
|
|
552
|
-
error_description: "request and request_uri cannot both be present in a JAR request"
|
|
553
|
-
});
|
|
554
|
-
if (!jarRequestParams.request && !jarRequestParams.request_uri) throw new Oauth2ServerErrorResponseError({
|
|
555
|
-
error: Oauth2ErrorCodes.InvalidRequestObject,
|
|
556
|
-
error_description: "request or request_uri must be present"
|
|
557
|
-
});
|
|
558
|
-
return jarRequestParams;
|
|
559
|
-
}
|
|
560
|
-
function isJarAuthorizationRequest(request) {
|
|
561
|
-
return "request" in request || "request_uri" in request;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
//#endregion
|
|
565
|
-
//#region src/jar/z-jar-request-object.ts
|
|
566
|
-
const zJarRequestObjectPayload = zod.z.object({
|
|
567
|
-
...zJwtPayload.shape,
|
|
568
|
-
client_id: zod.z.string()
|
|
569
|
-
}).loose();
|
|
570
|
-
const zSignedAuthorizationRequestJwtHeaderTyp = zod.z.literal("oauth-authz-req+jwt");
|
|
571
|
-
const signedAuthorizationRequestJwtHeaderTyp = zSignedAuthorizationRequestJwtHeaderTyp.value;
|
|
572
|
-
const zJwtAuthorizationRequestJwtHeaderTyp = zod.z.literal("jwt");
|
|
573
|
-
const jwtAuthorizationRequestJwtHeaderTyp = zJwtAuthorizationRequestJwtHeaderTyp.value;
|
|
574
|
-
|
|
575
|
-
//#endregion
|
|
576
|
-
//#region src/jar/handle-jar-request/verify-jar-request.ts
|
|
577
|
-
/**
|
|
578
|
-
* Parse a JAR (JWT Secured Authorization Request) request by validating and optionally fetch from uri.
|
|
579
|
-
*
|
|
580
|
-
* @param options - The input parameters
|
|
581
|
-
* @param options.jarRequestParams - The JAR authorization request parameters
|
|
582
|
-
* @param options.callbacks - Context containing the relevant Jose crypto operations
|
|
583
|
-
* @returns An object containing the transmission method ('value' or 'reference') and the JWT request object.
|
|
584
|
-
*/
|
|
585
|
-
async function parseJarRequest(options) {
|
|
586
|
-
const { callbacks } = options;
|
|
587
|
-
const jarRequestParams = {
|
|
588
|
-
...validateJarRequestParams(options),
|
|
589
|
-
...options.jarRequestParams
|
|
590
|
-
};
|
|
591
|
-
return {
|
|
592
|
-
sendBy: jarRequestParams.request ? "value" : "reference",
|
|
593
|
-
authorizationRequestJwt: jarRequestParams.request ?? await fetchJarRequestObject({
|
|
594
|
-
requestUri: jarRequestParams.request_uri,
|
|
595
|
-
fetch: callbacks.fetch
|
|
596
|
-
})
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
/**
|
|
600
|
-
* Verifies a JAR (JWT Secured Authorization Request) request by validating and verifying signatures.
|
|
601
|
-
*
|
|
602
|
-
* @param options - The input parameters
|
|
603
|
-
* @param options.jarRequestParams - The JAR authorization request parameters
|
|
604
|
-
* @param options.callbacks - Context containing the relevant Jose crypto operations
|
|
605
|
-
* @returns The verified authorization request parameters and metadata
|
|
606
|
-
*/
|
|
607
|
-
async function verifyJarRequest(options) {
|
|
608
|
-
const { jarRequestParams, authorizationRequestJwt, callbacks, jwtSigner } = options;
|
|
609
|
-
if (zCompactJwe.safeParse(authorizationRequestJwt).success) throw new Oauth2ServerErrorResponseError({
|
|
610
|
-
error: Oauth2ErrorCodes.InvalidRequestObject,
|
|
611
|
-
error_description: "Encrypted JWE request objects are not supported."
|
|
612
|
-
});
|
|
613
|
-
if (!zCompactJwt.safeParse(authorizationRequestJwt).success) throw new Oauth2ServerErrorResponseError({
|
|
614
|
-
error: Oauth2ErrorCodes.InvalidRequestObject,
|
|
615
|
-
error_description: "JAR request object is not a valid JWT."
|
|
616
|
-
});
|
|
617
|
-
const { authorizationRequestPayload, signer, jwt } = await verifyJarRequestObject({
|
|
618
|
-
authorizationRequestJwt,
|
|
619
|
-
callbacks,
|
|
620
|
-
jwtSigner
|
|
621
|
-
});
|
|
622
|
-
if (!authorizationRequestPayload.client_id) throw new Oauth2ServerErrorResponseError({
|
|
623
|
-
error: Oauth2ErrorCodes.InvalidRequestObject,
|
|
624
|
-
error_description: "Jar Request Object is missing the required \"client_id\" field."
|
|
625
|
-
});
|
|
626
|
-
if (jarRequestParams.client_id !== authorizationRequestPayload.client_id) throw new Oauth2ServerErrorResponseError({
|
|
627
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
628
|
-
error_description: "client_id does not match the request object client_id."
|
|
629
|
-
});
|
|
630
|
-
return {
|
|
631
|
-
jwt,
|
|
632
|
-
authorizationRequestPayload,
|
|
633
|
-
signer
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
async function fetchJarRequestObject(options) {
|
|
637
|
-
const { requestUri, fetch } = options;
|
|
638
|
-
const response = await (0, __openid4vc_utils.createFetcher)(fetch)(requestUri, {
|
|
639
|
-
method: "get",
|
|
640
|
-
headers: {
|
|
641
|
-
Accept: `${__openid4vc_utils.ContentType.OAuthAuthorizationRequestJwt}, ${__openid4vc_utils.ContentType.Jwt};q=0.9, text/plain`,
|
|
642
|
-
"Content-Type": __openid4vc_utils.ContentType.XWwwFormUrlencoded
|
|
643
|
-
}
|
|
644
|
-
}).catch(() => {
|
|
645
|
-
throw new Oauth2ServerErrorResponseError({
|
|
646
|
-
error_description: `Fetching request_object from request_uri '${requestUri}' failed`,
|
|
647
|
-
error: Oauth2ErrorCodes.InvalidRequestUri
|
|
648
|
-
});
|
|
649
|
-
});
|
|
650
|
-
if (!response.ok) throw new Oauth2ServerErrorResponseError({
|
|
651
|
-
error_description: `Fetching request_object from request_uri '${requestUri}' failed with status code '${response.status}'.`,
|
|
652
|
-
error: Oauth2ErrorCodes.InvalidRequestUri
|
|
653
|
-
});
|
|
654
|
-
return await response.text();
|
|
655
|
-
}
|
|
656
|
-
async function verifyJarRequestObject(options) {
|
|
657
|
-
const { authorizationRequestJwt, callbacks, jwtSigner } = options;
|
|
658
|
-
const jwt = decodeJwt({
|
|
659
|
-
jwt: authorizationRequestJwt,
|
|
660
|
-
payloadSchema: zJarRequestObjectPayload
|
|
661
|
-
});
|
|
662
|
-
const { signer } = await verifyJwt({
|
|
663
|
-
verifyJwtCallback: callbacks.verifyJwt,
|
|
664
|
-
compact: authorizationRequestJwt,
|
|
665
|
-
header: jwt.header,
|
|
666
|
-
payload: jwt.payload,
|
|
667
|
-
signer: jwtSigner
|
|
668
|
-
});
|
|
669
|
-
if (jwt.header.typ !== signedAuthorizationRequestJwtHeaderTyp && jwt.header.typ !== jwtAuthorizationRequestJwtHeaderTyp) throw new Oauth2ServerErrorResponseError({
|
|
670
|
-
error: Oauth2ErrorCodes.InvalidRequestObject,
|
|
671
|
-
error_description: `Invalid Jar Request Object typ header. Expected "oauth-authz-req+jwt" or "jwt", received "${jwt.header.typ}".`
|
|
672
|
-
});
|
|
673
|
-
return {
|
|
674
|
-
signer,
|
|
675
|
-
jwt,
|
|
676
|
-
authorizationRequestPayload: jwt.payload
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
//#endregion
|
|
681
|
-
//#region src/client-attestation/z-client-attestation.ts
|
|
682
|
-
const zOauthClientAttestationHeader = zod.default.literal("OAuth-Client-Attestation");
|
|
683
|
-
const oauthClientAttestationHeader = zOauthClientAttestationHeader.value;
|
|
684
|
-
const zClientAttestationJwtPayload = zod.default.object({
|
|
685
|
-
...zJwtPayload.shape,
|
|
686
|
-
iss: zod.default.string(),
|
|
687
|
-
sub: zod.default.string(),
|
|
688
|
-
exp: __openid4vc_utils.zInteger,
|
|
689
|
-
cnf: zod.default.object({ jwk: zJwk }).loose(),
|
|
690
|
-
wallet_name: zod.default.string().optional(),
|
|
691
|
-
wallet_link: zod.default.url().optional()
|
|
692
|
-
}).loose();
|
|
693
|
-
const zClientAttestationJwtHeader = zod.default.object({
|
|
694
|
-
...zJwtHeader.shape,
|
|
695
|
-
typ: zod.default.literal("oauth-client-attestation+jwt")
|
|
696
|
-
}).loose();
|
|
697
|
-
const zOauthClientAttestationPopHeader = zod.default.literal("OAuth-Client-Attestation-PoP");
|
|
698
|
-
const oauthClientAttestationPopHeader = zOauthClientAttestationPopHeader.value;
|
|
699
|
-
const zClientAttestationPopJwtPayload = zod.default.object({
|
|
700
|
-
...zJwtPayload.shape,
|
|
701
|
-
iss: zod.default.string(),
|
|
702
|
-
exp: __openid4vc_utils.zInteger,
|
|
703
|
-
aud: zod.default.union([__openid4vc_utils.zHttpsUrl, zod.default.array(__openid4vc_utils.zHttpsUrl)]),
|
|
704
|
-
jti: zod.default.string(),
|
|
705
|
-
nonce: zod.default.optional(zod.default.string())
|
|
706
|
-
}).loose();
|
|
707
|
-
const zClientAttestationPopJwtHeader = zod.default.object({
|
|
708
|
-
...zJwtHeader.shape,
|
|
709
|
-
typ: zod.default.literal("oauth-client-attestation-pop+jwt")
|
|
710
|
-
}).loose();
|
|
711
|
-
|
|
712
|
-
//#endregion
|
|
713
|
-
//#region src/client-attestation/client-attestation-pop.ts
|
|
714
|
-
async function verifyClientAttestationPopJwt(options) {
|
|
715
|
-
const { header, payload } = decodeJwt({
|
|
716
|
-
jwt: options.clientAttestationPopJwt,
|
|
717
|
-
headerSchema: zClientAttestationPopJwtHeader,
|
|
718
|
-
payloadSchema: zClientAttestationPopJwtPayload
|
|
719
|
-
});
|
|
720
|
-
if (payload.iss !== options.clientAttestation.payload.sub) throw new Oauth2Error(`Client Attestation Pop jwt contains 'iss' (client_id) value '${payload.iss}', but expected 'sub' value from client attestation '${options.clientAttestation.payload.sub}'`);
|
|
721
|
-
const { signer } = await verifyJwt({
|
|
722
|
-
signer: {
|
|
723
|
-
alg: header.alg,
|
|
724
|
-
method: "jwk",
|
|
725
|
-
publicJwk: options.clientAttestation.payload.cnf.jwk
|
|
726
|
-
},
|
|
727
|
-
now: options.now,
|
|
728
|
-
header,
|
|
729
|
-
expectedNonce: options.expectedNonce,
|
|
730
|
-
payload,
|
|
731
|
-
expectedAudience: options.authorizationServer,
|
|
732
|
-
compact: options.clientAttestationPopJwt,
|
|
733
|
-
verifyJwtCallback: options.callbacks.verifyJwt,
|
|
734
|
-
errorMessage: "client attestation pop jwt verification failed"
|
|
735
|
-
});
|
|
736
|
-
return {
|
|
737
|
-
header,
|
|
738
|
-
payload,
|
|
739
|
-
signer
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
async function createClientAttestationPopJwt(options) {
|
|
743
|
-
const clientAttestation = decodeJwt({
|
|
744
|
-
jwt: options.clientAttestation,
|
|
745
|
-
headerSchema: zClientAttestationJwtHeader,
|
|
746
|
-
payloadSchema: zClientAttestationJwtPayload
|
|
747
|
-
});
|
|
748
|
-
const signer = options.signer ?? {
|
|
749
|
-
method: "jwk",
|
|
750
|
-
alg: clientAttestation.header.alg,
|
|
751
|
-
publicJwk: clientAttestation.payload.cnf.jwk
|
|
752
|
-
};
|
|
753
|
-
const header = (0, __openid4vc_utils.parseWithErrorHandling)(zClientAttestationPopJwtHeader, {
|
|
754
|
-
typ: "oauth-client-attestation-pop+jwt",
|
|
755
|
-
alg: signer.alg
|
|
756
|
-
});
|
|
757
|
-
const expiresAt = options.expiresAt ?? (0, __openid4vc_utils.addSecondsToDate)(options.issuedAt ?? /* @__PURE__ */ new Date(), 60);
|
|
758
|
-
const payload = (0, __openid4vc_utils.parseWithErrorHandling)(zClientAttestationPopJwtPayload, {
|
|
759
|
-
aud: options.authorizationServer,
|
|
760
|
-
iss: clientAttestation.payload.sub,
|
|
761
|
-
iat: (0, __openid4vc_utils.dateToSeconds)(options.issuedAt),
|
|
762
|
-
exp: (0, __openid4vc_utils.dateToSeconds)(expiresAt),
|
|
763
|
-
jti: (0, __openid4vc_utils.encodeToBase64Url)(await options.callbacks.generateRandom(32)),
|
|
764
|
-
nonce: options.nonce,
|
|
765
|
-
...options.additionalPayload
|
|
766
|
-
});
|
|
767
|
-
const { jwt } = await options.callbacks.signJwt(signer, {
|
|
768
|
-
header,
|
|
769
|
-
payload
|
|
770
|
-
});
|
|
771
|
-
return jwt;
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
//#endregion
|
|
775
|
-
//#region src/client-attestation/client-attestation.ts
|
|
776
|
-
async function verifyClientAttestationJwt(options) {
|
|
777
|
-
const { header, payload } = decodeJwt({
|
|
778
|
-
jwt: options.clientAttestationJwt,
|
|
779
|
-
headerSchema: zClientAttestationJwtHeader,
|
|
780
|
-
payloadSchema: zClientAttestationJwtPayload
|
|
781
|
-
});
|
|
782
|
-
const { signer } = await verifyJwt({
|
|
783
|
-
signer: jwtSignerFromJwt({
|
|
784
|
-
header,
|
|
785
|
-
payload
|
|
786
|
-
}),
|
|
787
|
-
now: options.now,
|
|
788
|
-
header,
|
|
789
|
-
payload,
|
|
790
|
-
compact: options.clientAttestationJwt,
|
|
791
|
-
verifyJwtCallback: options.callbacks.verifyJwt,
|
|
792
|
-
errorMessage: "client attestation jwt verification failed."
|
|
793
|
-
});
|
|
794
|
-
return {
|
|
795
|
-
header,
|
|
796
|
-
payload,
|
|
797
|
-
signer
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
|
-
async function createClientAttestationJwt(options) {
|
|
801
|
-
const header = (0, __openid4vc_utils.parseWithErrorHandling)(zClientAttestationJwtHeader, {
|
|
802
|
-
typ: "oauth-client-attestation+jwt",
|
|
803
|
-
...jwtHeaderFromJwtSigner(options.signer)
|
|
804
|
-
});
|
|
805
|
-
const payload = (0, __openid4vc_utils.parseWithErrorHandling)(zClientAttestationJwtPayload, {
|
|
806
|
-
iss: options.issuer,
|
|
807
|
-
iat: (0, __openid4vc_utils.dateToSeconds)(options.issuedAt),
|
|
808
|
-
exp: (0, __openid4vc_utils.dateToSeconds)(options.expiresAt),
|
|
809
|
-
sub: options.clientId,
|
|
810
|
-
cnf: options.confirmation,
|
|
811
|
-
...options.additionalPayload
|
|
812
|
-
});
|
|
813
|
-
const { jwt } = await options.callbacks.signJwt(options.signer, {
|
|
814
|
-
header,
|
|
815
|
-
payload
|
|
816
|
-
});
|
|
817
|
-
return jwt;
|
|
818
|
-
}
|
|
819
|
-
function extractClientAttestationJwtsFromHeaders(headers) {
|
|
820
|
-
const clientAttestationHeader = headers.get(oauthClientAttestationHeader);
|
|
821
|
-
const clientAttestationPopHeader = headers.get(oauthClientAttestationPopHeader);
|
|
822
|
-
if (!clientAttestationHeader && !clientAttestationPopHeader) return { valid: true };
|
|
823
|
-
if (!clientAttestationHeader || !clientAttestationPopHeader) return { valid: false };
|
|
824
|
-
if (!zCompactJwt.safeParse(clientAttestationHeader).success || !zCompactJwt.safeParse(clientAttestationPopHeader).success) return { valid: false };
|
|
825
|
-
return {
|
|
826
|
-
valid: true,
|
|
827
|
-
clientAttestationPopHeader,
|
|
828
|
-
clientAttestationHeader
|
|
829
|
-
};
|
|
830
|
-
}
|
|
831
|
-
async function verifyClientAttestation({ authorizationServer, clientAttestationJwt, clientAttestationPopJwt, callbacks, now }) {
|
|
832
|
-
try {
|
|
833
|
-
const clientAttestation = await verifyClientAttestationJwt({
|
|
834
|
-
callbacks,
|
|
835
|
-
clientAttestationJwt,
|
|
836
|
-
now
|
|
837
|
-
});
|
|
838
|
-
return {
|
|
839
|
-
clientAttestation,
|
|
840
|
-
clientAttestationPop: await verifyClientAttestationPopJwt({
|
|
841
|
-
callbacks,
|
|
842
|
-
authorizationServer,
|
|
843
|
-
clientAttestation,
|
|
844
|
-
clientAttestationPopJwt,
|
|
845
|
-
now
|
|
846
|
-
})
|
|
847
|
-
};
|
|
848
|
-
} catch (error) {
|
|
849
|
-
if (error instanceof Oauth2Error) throw new Oauth2ServerErrorResponseError({
|
|
850
|
-
error: Oauth2ErrorCodes.InvalidClient,
|
|
851
|
-
error_description: `Error verifying client attestation. ${error.message}`
|
|
852
|
-
}, {
|
|
853
|
-
status: 401,
|
|
854
|
-
cause: error
|
|
855
|
-
});
|
|
856
|
-
throw new Oauth2ServerErrorResponseError({
|
|
857
|
-
error: Oauth2ErrorCodes.ServerError,
|
|
858
|
-
error_description: "Error during verification of client attestation jwt"
|
|
859
|
-
}, {
|
|
860
|
-
status: 500,
|
|
861
|
-
cause: error,
|
|
862
|
-
internalMessage: "Unknown error thrown during verification of client attestation jwt"
|
|
863
|
-
});
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
//#endregion
|
|
868
|
-
//#region src/dpop/z-dpop.ts
|
|
869
|
-
const zDpopJwtPayload = zod.default.object({
|
|
870
|
-
...zJwtPayload.shape,
|
|
871
|
-
iat: __openid4vc_utils.zInteger,
|
|
872
|
-
htu: __openid4vc_utils.zHttpsUrl,
|
|
873
|
-
htm: __openid4vc_utils.zHttpMethod,
|
|
874
|
-
jti: zod.default.string(),
|
|
875
|
-
ath: zod.default.optional(zod.default.string())
|
|
876
|
-
}).loose();
|
|
877
|
-
const zDpopJwtHeader = zod.default.object({
|
|
878
|
-
...zJwtHeader.shape,
|
|
879
|
-
typ: zod.default.literal("dpop+jwt"),
|
|
880
|
-
jwk: zJwk
|
|
881
|
-
}).loose();
|
|
882
|
-
|
|
883
|
-
//#endregion
|
|
884
|
-
//#region src/dpop/dpop.ts
|
|
885
|
-
async function createDpopHeadersForRequest(options) {
|
|
886
|
-
return { DPoP: await createDpopJwt(options) };
|
|
887
|
-
}
|
|
888
|
-
async function createDpopJwt(options) {
|
|
889
|
-
let ath;
|
|
890
|
-
if (options.accessToken) ath = (0, __openid4vc_utils.encodeToBase64Url)(await options.callbacks.hash((0, __openid4vc_utils.decodeUtf8String)(options.accessToken), HashAlgorithm.Sha256));
|
|
891
|
-
const header = (0, __openid4vc_utils.parseWithErrorHandling)(zDpopJwtHeader, {
|
|
892
|
-
typ: "dpop+jwt",
|
|
893
|
-
jwk: options.signer.publicJwk,
|
|
894
|
-
alg: options.signer.alg
|
|
895
|
-
});
|
|
896
|
-
const payload = (0, __openid4vc_utils.parseWithErrorHandling)(zDpopJwtPayload, {
|
|
897
|
-
htu: htuFromRequestUrl(options.request.url),
|
|
898
|
-
iat: (0, __openid4vc_utils.dateToSeconds)(options.issuedAt),
|
|
899
|
-
htm: options.request.method,
|
|
900
|
-
jti: (0, __openid4vc_utils.encodeToBase64Url)(await options.callbacks.generateRandom(32)),
|
|
901
|
-
ath,
|
|
902
|
-
nonce: options.nonce,
|
|
903
|
-
...options.additionalPayload
|
|
904
|
-
});
|
|
905
|
-
const { jwt } = await options.callbacks.signJwt(options.signer, {
|
|
906
|
-
header,
|
|
907
|
-
payload
|
|
908
|
-
});
|
|
909
|
-
return jwt;
|
|
910
|
-
}
|
|
911
|
-
async function verifyDpopJwt(options) {
|
|
912
|
-
try {
|
|
913
|
-
const { header, payload } = decodeJwt({
|
|
914
|
-
jwt: options.dpopJwt,
|
|
915
|
-
headerSchema: zDpopJwtHeader,
|
|
916
|
-
payloadSchema: zDpopJwtPayload
|
|
917
|
-
});
|
|
918
|
-
if (options.allowedSigningAlgs && !options.allowedSigningAlgs.includes(header.alg)) throw new Oauth2Error(`dpop jwt uses alg value '${header.alg}' but allowed dpop signging alg values are ${options.allowedSigningAlgs.join(", ")}.`);
|
|
919
|
-
if (options.expectedNonce) {
|
|
920
|
-
if (!payload.nonce) throw new Oauth2Error(`Dpop jwt does not have a nonce value, but expected nonce value '${options.expectedNonce}'`);
|
|
921
|
-
if (payload.nonce !== options.expectedNonce) throw new Oauth2Error(`Dpop jwt contains nonce value '${payload.nonce}', but expected nonce value '${options.expectedNonce}'`);
|
|
922
|
-
}
|
|
923
|
-
if (options.request.method !== payload.htm) throw new Oauth2Error(`Dpop jwt contains htm value '${payload.htm}', but expected htm value '${options.request.method}'`);
|
|
924
|
-
const expectedHtu = htuFromRequestUrl(options.request.url);
|
|
925
|
-
if (expectedHtu !== payload.htu) throw new Oauth2Error(`Dpop jwt contains htu value '${payload.htu}', but expected htu value '${expectedHtu}'.`);
|
|
926
|
-
if (options.accessToken) {
|
|
927
|
-
const expectedAth = (0, __openid4vc_utils.encodeToBase64Url)(await options.callbacks.hash((0, __openid4vc_utils.decodeUtf8String)(options.accessToken), HashAlgorithm.Sha256));
|
|
928
|
-
if (!payload.ath) throw new Oauth2Error(`Dpop jwt does not have a ath value, but expected ath value '${expectedAth}'.`);
|
|
929
|
-
if (payload.ath !== expectedAth) throw new Oauth2Error(`Dpop jwt contains ath value '${payload.ath}', but expected ath value '${expectedAth}'.`);
|
|
930
|
-
}
|
|
931
|
-
const jwkThumbprint = await calculateJwkThumbprint({
|
|
932
|
-
hashAlgorithm: HashAlgorithm.Sha256,
|
|
933
|
-
hashCallback: options.callbacks.hash,
|
|
934
|
-
jwk: header.jwk
|
|
935
|
-
});
|
|
936
|
-
if (options.expectedJwkThumbprint && options.expectedJwkThumbprint !== jwkThumbprint) throw new Oauth2Error(`Dpop is signed with jwk with thumbprint value '${jwkThumbprint}', but expect jwk thumbprint value '${options.expectedJwkThumbprint}'`);
|
|
937
|
-
await verifyJwt({
|
|
938
|
-
signer: {
|
|
939
|
-
alg: header.alg,
|
|
940
|
-
method: "jwk",
|
|
941
|
-
publicJwk: header.jwk
|
|
942
|
-
},
|
|
943
|
-
now: options.now,
|
|
944
|
-
header,
|
|
945
|
-
payload,
|
|
946
|
-
compact: options.dpopJwt,
|
|
947
|
-
verifyJwtCallback: options.callbacks.verifyJwt,
|
|
948
|
-
errorMessage: "dpop jwt verification failed"
|
|
949
|
-
});
|
|
950
|
-
return {
|
|
951
|
-
header,
|
|
952
|
-
payload,
|
|
953
|
-
jwkThumbprint
|
|
954
|
-
};
|
|
955
|
-
} catch (error) {
|
|
956
|
-
if (error instanceof Oauth2Error) throw new Oauth2ServerErrorResponseError({
|
|
957
|
-
error: Oauth2ErrorCodes.InvalidDpopProof,
|
|
958
|
-
error_description: error.message
|
|
959
|
-
});
|
|
960
|
-
throw error;
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
function htuFromRequestUrl(requestUrl) {
|
|
964
|
-
const htu = new __openid4vc_utils.URL(requestUrl);
|
|
965
|
-
htu.search = "";
|
|
966
|
-
htu.hash = "";
|
|
967
|
-
return htu.toString();
|
|
968
|
-
}
|
|
969
|
-
function extractDpopNonceFromHeaders(headers) {
|
|
970
|
-
return headers.get("DPoP-Nonce");
|
|
971
|
-
}
|
|
972
|
-
function extractDpopJwtFromHeaders(headers) {
|
|
973
|
-
const dpopJwt = headers.get("DPoP");
|
|
974
|
-
if (!dpopJwt) return { valid: true };
|
|
975
|
-
if (!zCompactJwt.safeParse(dpopJwt).success) return { valid: false };
|
|
976
|
-
return {
|
|
977
|
-
valid: true,
|
|
978
|
-
dpopJwt
|
|
979
|
-
};
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
//#endregion
|
|
983
|
-
//#region src/authorization-request/parse-authorization-request.ts
|
|
984
|
-
/**
|
|
985
|
-
* Parse an authorization request.
|
|
986
|
-
*
|
|
987
|
-
* @throws {Oauth2ServerErrorResponseError}
|
|
988
|
-
*/
|
|
989
|
-
function parseAuthorizationRequest(options) {
|
|
990
|
-
const extractedDpopJwt = extractDpopJwtFromHeaders(options.request.headers);
|
|
991
|
-
if (!extractedDpopJwt.valid) throw new Oauth2ServerErrorResponseError({
|
|
992
|
-
error: Oauth2ErrorCodes.InvalidDpopProof,
|
|
993
|
-
error_description: `Request contains a 'DPoP' header, but the value is not a valid DPoP jwt`
|
|
994
|
-
});
|
|
995
|
-
const extractedClientAttestationJwts = extractClientAttestationJwtsFromHeaders(options.request.headers);
|
|
996
|
-
if (!extractedClientAttestationJwts.valid) throw new Oauth2ServerErrorResponseError({
|
|
997
|
-
error: Oauth2ErrorCodes.InvalidClient,
|
|
998
|
-
error_description: "Request contains client attestation header, but the values are not valid client attestation and client attestation PoP header."
|
|
999
|
-
});
|
|
1000
|
-
return {
|
|
1001
|
-
dpop: extractedDpopJwt.dpopJwt ? {
|
|
1002
|
-
jwt: extractedDpopJwt.dpopJwt,
|
|
1003
|
-
jwkThumbprint: options.authorizationRequest.dpop_jkt
|
|
1004
|
-
} : options.authorizationRequest.dpop_jkt ? {
|
|
1005
|
-
jwt: extractedDpopJwt.dpopJwt,
|
|
1006
|
-
jwkThumbprint: options.authorizationRequest.dpop_jkt
|
|
1007
|
-
} : void 0,
|
|
1008
|
-
clientAttestation: extractedClientAttestationJwts.clientAttestationHeader ? {
|
|
1009
|
-
clientAttestationJwt: extractedClientAttestationJwts.clientAttestationHeader,
|
|
1010
|
-
clientAttestationPopJwt: extractedClientAttestationJwts.clientAttestationPopHeader
|
|
1011
|
-
} : void 0
|
|
1012
|
-
};
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
//#endregion
|
|
1016
|
-
//#region src/authorization-request/z-authorization-request.ts
|
|
1017
|
-
const zPushedAuthorizationRequestUriPrefix = zod.default.literal("urn:ietf:params:oauth:request_uri:");
|
|
1018
|
-
const pushedAuthorizationRequestUriPrefix = zPushedAuthorizationRequestUriPrefix.value;
|
|
1019
|
-
const zAuthorizationRequest = zod.default.object({
|
|
1020
|
-
response_type: zod.default.string(),
|
|
1021
|
-
client_id: zod.default.string(),
|
|
1022
|
-
issuer_state: zod.default.optional(zod.default.string()),
|
|
1023
|
-
redirect_uri: zod.default.url().optional(),
|
|
1024
|
-
resource: zod.default.optional(__openid4vc_utils.zHttpsUrl),
|
|
1025
|
-
scope: zod.default.optional(zod.default.string()),
|
|
1026
|
-
state: zod.default.optional(zod.default.string()),
|
|
1027
|
-
dpop_jkt: zod.default.optional(zod.default.base64url()),
|
|
1028
|
-
code_challenge: zod.default.optional(zod.default.string()),
|
|
1029
|
-
code_challenge_method: zod.default.optional(zod.default.string())
|
|
1030
|
-
}).loose();
|
|
1031
|
-
const zPushedAuthorizationRequest = zod.default.object({
|
|
1032
|
-
request_uri: zod.default.string(),
|
|
1033
|
-
client_id: zod.default.string()
|
|
1034
|
-
}).loose();
|
|
1035
|
-
const zPushedAuthorizationResponse = zod.default.object({
|
|
1036
|
-
request_uri: zod.default.string(),
|
|
1037
|
-
expires_in: zod.default.number().int()
|
|
1038
|
-
}).loose();
|
|
1039
|
-
|
|
1040
|
-
//#endregion
|
|
1041
|
-
//#region src/authorization-request/parse-pushed-authorization-request.ts
|
|
1042
|
-
/**
|
|
1043
|
-
* Parse an pushed authorization request.
|
|
1044
|
-
*
|
|
1045
|
-
* @throws {Oauth2ServerErrorResponseError}
|
|
1046
|
-
*/
|
|
1047
|
-
async function parsePushedAuthorizationRequest(options) {
|
|
1048
|
-
const parsed = (0, __openid4vc_utils.parseWithErrorHandling)(zod.default.union([zAuthorizationRequest, zJarAuthorizationRequest]), options.authorizationRequest, "Invalid authorization request. Could not parse authorization request or jar.");
|
|
1049
|
-
let parsedAuthorizationRequest;
|
|
1050
|
-
let authorizationRequestJwt;
|
|
1051
|
-
if (isJarAuthorizationRequest(parsed)) {
|
|
1052
|
-
const parsedJar = await parseJarRequest({
|
|
1053
|
-
jarRequestParams: parsed,
|
|
1054
|
-
callbacks: options.callbacks
|
|
1055
|
-
});
|
|
1056
|
-
const jwt = decodeJwt({ jwt: parsedJar.authorizationRequestJwt });
|
|
1057
|
-
parsedAuthorizationRequest = zAuthorizationRequest.safeParse(jwt.payload);
|
|
1058
|
-
if (!parsedAuthorizationRequest.success) throw new Oauth2ServerErrorResponseError({
|
|
1059
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
1060
|
-
error_description: `Invalid authorization request. Could not parse jar request payload.\n${(0, __openid4vc_utils.formatZodError)(parsedAuthorizationRequest.error)}`
|
|
1061
|
-
});
|
|
1062
|
-
authorizationRequestJwt = parsedJar.authorizationRequestJwt;
|
|
1063
|
-
} else {
|
|
1064
|
-
parsedAuthorizationRequest = zAuthorizationRequest.safeParse(options.authorizationRequest);
|
|
1065
|
-
if (!parsedAuthorizationRequest.success) throw new Oauth2ServerErrorResponseError({
|
|
1066
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
1067
|
-
error_description: `Error occurred during validation of pushed authorization request.\n${(0, __openid4vc_utils.formatZodError)(parsedAuthorizationRequest.error)}`
|
|
1068
|
-
});
|
|
1069
|
-
}
|
|
1070
|
-
const authorizationRequest = parsedAuthorizationRequest.data;
|
|
1071
|
-
const { clientAttestation, dpop } = parseAuthorizationRequest({
|
|
1072
|
-
authorizationRequest,
|
|
1073
|
-
request: options.request
|
|
1074
|
-
});
|
|
1075
|
-
return {
|
|
1076
|
-
authorizationRequest,
|
|
1077
|
-
authorizationRequestJwt,
|
|
1078
|
-
dpop,
|
|
1079
|
-
clientAttestation
|
|
1080
|
-
};
|
|
1081
|
-
}
|
|
1082
|
-
/**
|
|
1083
|
-
* Parse a pushed authorization request URI prefixed with `urn:ietf:params:oauth:request_uri:`
|
|
1084
|
-
* and returns the identifier, without the prefix.
|
|
1085
|
-
*
|
|
1086
|
-
* @throws {Oauth2ServerErrorResponseError}
|
|
1087
|
-
*/
|
|
1088
|
-
function parsePushedAuthorizationRequestUriReferenceValue(options) {
|
|
1089
|
-
if (!options.uri.startsWith(pushedAuthorizationRequestUriPrefix)) throw new Oauth2ServerErrorResponseError({
|
|
1090
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
1091
|
-
error_description: `The 'request_uri' must start with the prefix "${pushedAuthorizationRequestUriPrefix}".`
|
|
1092
|
-
});
|
|
1093
|
-
return options.uri.substring(pushedAuthorizationRequestUriPrefix.length);
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
//#endregion
|
|
1097
|
-
//#region src/authorization-response/z-authorization-response.ts
|
|
1098
|
-
const zAuthorizationResponse = zod.default.object({
|
|
1099
|
-
state: zod.default.string().optional(),
|
|
1100
|
-
code: zod.default.string().nonempty(),
|
|
1101
|
-
error: zod.default.optional(zod.default.never())
|
|
1102
|
-
}).loose();
|
|
1103
|
-
const zAuthorizationResponseFromUriParams = zod.default.url().transform((url) => Object.fromEntries(new __openid4vc_utils.URL(url).searchParams)).pipe(zAuthorizationResponse);
|
|
1104
|
-
const zAuthorizationErrorResponse = zod.default.object({
|
|
1105
|
-
...zOauth2ErrorResponse.shape,
|
|
1106
|
-
state: zod.default.string().optional(),
|
|
1107
|
-
code: zod.default.optional(zod.default.never())
|
|
1108
|
-
}).loose();
|
|
1109
|
-
|
|
1110
|
-
//#endregion
|
|
1111
|
-
//#region src/authorization-response/parse-authorization-response.ts
|
|
1112
|
-
/**
|
|
1113
|
-
* Parse an authorization response redirect URL.
|
|
1114
|
-
*
|
|
1115
|
-
* @throws {Oauth2ServerErrorResponseError}
|
|
1116
|
-
*/
|
|
1117
|
-
function parseAuthorizationResponseRedirectUrl(options) {
|
|
1118
|
-
const searchParams = Object.fromEntries(new __openid4vc_utils.URL(options.url).searchParams);
|
|
1119
|
-
const parsedAuthorizationResponse = zod.default.union([zAuthorizationErrorResponse, zAuthorizationResponse]).safeParse(searchParams);
|
|
1120
|
-
if (!parsedAuthorizationResponse.success) throw new Oauth2ServerErrorResponseError({
|
|
1121
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
1122
|
-
error_description: `Error occurred during validation of authorization response redirect URL.\n${(0, __openid4vc_utils.formatZodError)(parsedAuthorizationResponse.error)}`
|
|
1123
|
-
});
|
|
1124
|
-
return parsedAuthorizationResponse.data;
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
//#endregion
|
|
1128
|
-
//#region src/z-grant-type.ts
|
|
1129
|
-
const zPreAuthorizedCodeGrantIdentifier = zod.default.literal("urn:ietf:params:oauth:grant-type:pre-authorized_code");
|
|
1130
|
-
const preAuthorizedCodeGrantIdentifier = zPreAuthorizedCodeGrantIdentifier.value;
|
|
1131
|
-
const zAuthorizationCodeGrantIdentifier = zod.default.literal("authorization_code");
|
|
1132
|
-
const authorizationCodeGrantIdentifier = zAuthorizationCodeGrantIdentifier.value;
|
|
1133
|
-
const zRefreshTokenGrantIdentifier = zod.default.literal("refresh_token");
|
|
1134
|
-
const refreshTokenGrantIdentifier = zRefreshTokenGrantIdentifier.value;
|
|
1135
|
-
|
|
1136
|
-
//#endregion
|
|
1137
|
-
//#region src/client-authentication.ts
|
|
1138
|
-
let SupportedClientAuthenticationMethod = /* @__PURE__ */ function(SupportedClientAuthenticationMethod$1) {
|
|
1139
|
-
SupportedClientAuthenticationMethod$1["ClientSecretBasic"] = "client_secret_basic";
|
|
1140
|
-
SupportedClientAuthenticationMethod$1["ClientSecretPost"] = "client_secret_post";
|
|
1141
|
-
SupportedClientAuthenticationMethod$1["ClientAttestationJwt"] = "attest_jwt_client_auth";
|
|
1142
|
-
SupportedClientAuthenticationMethod$1["None"] = "none";
|
|
1143
|
-
return SupportedClientAuthenticationMethod$1;
|
|
1144
|
-
}({});
|
|
1145
|
-
/**
|
|
1146
|
-
* Determine the supported client authentication method based on authorization
|
|
1147
|
-
* server metadata
|
|
1148
|
-
*/
|
|
1149
|
-
function getSupportedClientAuthenticationMethod(authorizationServer, endpointType) {
|
|
1150
|
-
if (endpointType === "introspection" && authorizationServer.introspection_endpoint_auth_methods_supported) {
|
|
1151
|
-
const supportedMethod = authorizationServer.introspection_endpoint_auth_methods_supported.find((m) => Object.values(SupportedClientAuthenticationMethod).includes(m));
|
|
1152
|
-
if (!supportedMethod) throw new Oauth2Error(`Authorization server metadata for issuer '${authorizationServer.issuer}' has 'introspection_endpoint_auth_methods_supported' metadata, but does not contain a supported value. Supported values are '${Object.values(SupportedClientAuthenticationMethod).join(", ")}', found values are '${authorizationServer.introspection_endpoint_auth_methods_supported.join(", ")}'`);
|
|
1153
|
-
return supportedMethod;
|
|
1154
|
-
}
|
|
1155
|
-
if (authorizationServer.token_endpoint_auth_methods_supported) {
|
|
1156
|
-
const supportedMethod = authorizationServer.token_endpoint_auth_methods_supported.find((m) => Object.values(SupportedClientAuthenticationMethod).includes(m));
|
|
1157
|
-
if (!supportedMethod) throw new Oauth2Error(`Authorization server metadata for issuer '${authorizationServer.issuer}' has 'token_endpoint_auth_methods_supported' metadata, but does not contain a supported value. Supported values are '${Object.values(SupportedClientAuthenticationMethod).join(", ")}', found values are '${authorizationServer.token_endpoint_auth_methods_supported.join(", ")}'`);
|
|
1158
|
-
return supportedMethod;
|
|
1159
|
-
}
|
|
1160
|
-
return SupportedClientAuthenticationMethod.ClientSecretBasic;
|
|
1161
|
-
}
|
|
1162
|
-
/**
|
|
1163
|
-
* Dynamicaly get the client authentication method based on endpoint type and authorization server.
|
|
1164
|
-
* Only `client_secret_post`, `client_secret_basic`, and `none` supported.
|
|
1165
|
-
*
|
|
1166
|
-
* It also supports anonymous access to the token endpoint for pre-authorized code flow
|
|
1167
|
-
* if the authorization server has enabled `pre-authorized_grant_anonymous_access_supported`
|
|
1168
|
-
*/
|
|
1169
|
-
function clientAuthenticationDynamic(options) {
|
|
1170
|
-
return (callbackOptions) => {
|
|
1171
|
-
const { url, authorizationServerMetadata, body } = callbackOptions;
|
|
1172
|
-
const endpointType = url === authorizationServerMetadata.introspection_endpoint ? "introspection" : url === authorizationServerMetadata.token_endpoint ? "token" : "endpoint";
|
|
1173
|
-
const method = getSupportedClientAuthenticationMethod(authorizationServerMetadata, endpointType);
|
|
1174
|
-
if (endpointType === "token" && body.grant_type === preAuthorizedCodeGrantIdentifier && authorizationServerMetadata["pre-authorized_grant_anonymous_access_supported"]) return clientAuthenticationAnonymous()(callbackOptions);
|
|
1175
|
-
if (method === SupportedClientAuthenticationMethod.ClientSecretBasic) return clientAuthenticationClientSecretBasic(options)(callbackOptions);
|
|
1176
|
-
if (method === SupportedClientAuthenticationMethod.ClientSecretPost) return clientAuthenticationClientSecretPost(options)(callbackOptions);
|
|
1177
|
-
if (method === SupportedClientAuthenticationMethod.None) return clientAuthenticationNone(options)(callbackOptions);
|
|
1178
|
-
throw new Oauth2Error(`Unsupported client auth method ${method}. Supported values are ${Object.values(SupportedClientAuthenticationMethod).join(", ")}`);
|
|
1179
|
-
};
|
|
1180
|
-
}
|
|
1181
|
-
/**
|
|
1182
|
-
* Client authentication using `client_secret_post` option
|
|
1183
|
-
*/
|
|
1184
|
-
function clientAuthenticationClientSecretPost(options) {
|
|
1185
|
-
return ({ body }) => {
|
|
1186
|
-
body.client_id = options.clientId;
|
|
1187
|
-
body.client_secret = options.clientSecret;
|
|
1188
|
-
};
|
|
1189
|
-
}
|
|
1190
|
-
/**
|
|
1191
|
-
* Client authentication using `client_secret_basic` option
|
|
1192
|
-
*/
|
|
1193
|
-
function clientAuthenticationClientSecretBasic(options) {
|
|
1194
|
-
return ({ headers }) => {
|
|
1195
|
-
const authorization = (0, __openid4vc_utils.encodeToBase64Url)((0, __openid4vc_utils.decodeUtf8String)(`${options.clientId}:${options.clientSecret}`));
|
|
1196
|
-
headers.set("Authorization", `Basic ${authorization}`);
|
|
1197
|
-
};
|
|
1198
|
-
}
|
|
1199
|
-
/**
|
|
1200
|
-
* Client authentication using `none` option
|
|
1201
|
-
*/
|
|
1202
|
-
function clientAuthenticationNone(options) {
|
|
1203
|
-
return ({ body }) => {
|
|
1204
|
-
body.client_id = options.clientId;
|
|
1205
|
-
};
|
|
1206
|
-
}
|
|
1207
|
-
/**
|
|
1208
|
-
* Anonymous client authentication
|
|
1209
|
-
*/
|
|
1210
|
-
function clientAuthenticationAnonymous() {
|
|
1211
|
-
return () => {};
|
|
1212
|
-
}
|
|
1213
|
-
/**
|
|
1214
|
-
* Client authentication using `attest_jwt_client_auth` option.
|
|
1215
|
-
*/
|
|
1216
|
-
function clientAuthenticationClientAttestationJwt(options) {
|
|
1217
|
-
return async ({ headers, authorizationServerMetadata }) => {
|
|
1218
|
-
const clientAttestationPop = await createClientAttestationPopJwt({
|
|
1219
|
-
authorizationServer: authorizationServerMetadata.issuer,
|
|
1220
|
-
callbacks: options.callbacks,
|
|
1221
|
-
clientAttestation: options.clientAttestationJwt
|
|
1222
|
-
});
|
|
1223
|
-
headers.set(oauthClientAttestationHeader, options.clientAttestationJwt);
|
|
1224
|
-
headers.set(oauthClientAttestationPopHeader, clientAttestationPop);
|
|
1225
|
-
};
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
//#endregion
|
|
1229
|
-
//#region src/common/algorithm/algorithm-transform.ts
|
|
1230
|
-
/**
|
|
1231
|
-
* Algorithm transformation utilities for JWA and COSE
|
|
1232
|
-
*
|
|
1233
|
-
* This module provides utilities to transform between JWA (JSON Web Algorithms)
|
|
1234
|
-
* signature algorithm identifiers and fully-specified COSE (CBOR Object Signing and Encryption)
|
|
1235
|
-
* algorithm identifiers.
|
|
1236
|
-
*
|
|
1237
|
-
* Based on RFC 9864: Fully-Specified Algorithms for JOSE and COSE
|
|
1238
|
-
* https://www.rfc-editor.org/rfc/rfc9864.html
|
|
1239
|
-
*/
|
|
1240
|
-
/**
|
|
1241
|
-
* JWA (JSON Web Algorithms) signature algorithm identifiers
|
|
1242
|
-
*
|
|
1243
|
-
* From RFC 7518 (JWA) and RFC 9864 (Fully-Specified Algorithms)
|
|
1244
|
-
*/
|
|
1245
|
-
var JwaSignatureAlgorithm = /* @__PURE__ */ function(JwaSignatureAlgorithm$1) {
|
|
1246
|
-
JwaSignatureAlgorithm$1["Ed25519"] = "Ed25519";
|
|
1247
|
-
JwaSignatureAlgorithm$1["Ed448"] = "Ed448";
|
|
1248
|
-
JwaSignatureAlgorithm$1["EdDSA"] = "EdDSA";
|
|
1249
|
-
JwaSignatureAlgorithm$1["ES256"] = "ES256";
|
|
1250
|
-
JwaSignatureAlgorithm$1["ES384"] = "ES384";
|
|
1251
|
-
JwaSignatureAlgorithm$1["ES512"] = "ES512";
|
|
1252
|
-
JwaSignatureAlgorithm$1["ES256K"] = "ES256K";
|
|
1253
|
-
JwaSignatureAlgorithm$1["RS256"] = "RS256";
|
|
1254
|
-
JwaSignatureAlgorithm$1["RS384"] = "RS384";
|
|
1255
|
-
JwaSignatureAlgorithm$1["RS512"] = "RS512";
|
|
1256
|
-
JwaSignatureAlgorithm$1["PS256"] = "PS256";
|
|
1257
|
-
JwaSignatureAlgorithm$1["PS384"] = "PS384";
|
|
1258
|
-
JwaSignatureAlgorithm$1["PS512"] = "PS512";
|
|
1259
|
-
return JwaSignatureAlgorithm$1;
|
|
1260
|
-
}(JwaSignatureAlgorithm || {});
|
|
1261
|
-
/**
|
|
1262
|
-
* Mapping of JWA signature algorithm identifiers to fully-specified COSE algorithm identifiers
|
|
1263
|
-
*
|
|
1264
|
-
* From RFC 9864:
|
|
1265
|
-
* - EdDSA algorithms (Section 2.2)
|
|
1266
|
-
* - ECDSA algorithms (Section 2.1) - JWA ECDSA algorithms are already fully-specified
|
|
1267
|
-
*
|
|
1268
|
-
* Note: JWA ECDSA algorithms (ES256, ES384, ES512) are already fully-specified,
|
|
1269
|
-
* while COSE ECDSA algorithms with the same names are polymorphic and deprecated.
|
|
1270
|
-
* The fully-specified COSE equivalents use different names (ESP256, ESP384, ESP512).
|
|
1271
|
-
*/
|
|
1272
|
-
const JWA_SIGNATURE_TO_COSE_ALGORITHM_MAP = {
|
|
1273
|
-
[JwaSignatureAlgorithm.Ed25519]: -19,
|
|
1274
|
-
[JwaSignatureAlgorithm.Ed448]: -53,
|
|
1275
|
-
[JwaSignatureAlgorithm.EdDSA]: -19,
|
|
1276
|
-
[JwaSignatureAlgorithm.ES256]: -9,
|
|
1277
|
-
[JwaSignatureAlgorithm.ES384]: -51,
|
|
1278
|
-
[JwaSignatureAlgorithm.ES512]: -52,
|
|
1279
|
-
[JwaSignatureAlgorithm.ES256K]: -47,
|
|
1280
|
-
[JwaSignatureAlgorithm.RS256]: -257,
|
|
1281
|
-
[JwaSignatureAlgorithm.RS384]: -258,
|
|
1282
|
-
[JwaSignatureAlgorithm.RS512]: -259,
|
|
1283
|
-
[JwaSignatureAlgorithm.PS256]: -37,
|
|
1284
|
-
[JwaSignatureAlgorithm.PS384]: -38,
|
|
1285
|
-
[JwaSignatureAlgorithm.PS512]: -39
|
|
1286
|
-
};
|
|
1287
|
-
/**
|
|
1288
|
-
* Mapping of COSE algorithm identifiers to JWA signature algorithm identifiers
|
|
1289
|
-
*
|
|
1290
|
-
* This is the inverse of JWA_SIGNATURE_TO_COSE_ALGORITHM_MAP, with additional entries
|
|
1291
|
-
* for deprecated polymorphic COSE algorithms that should be avoided.
|
|
1292
|
-
*/
|
|
1293
|
-
const COSE_TO_JWA_SIGNATURE_ALGORITHM_MAP = {
|
|
1294
|
-
[-19]: JwaSignatureAlgorithm.Ed25519,
|
|
1295
|
-
[-53]: JwaSignatureAlgorithm.Ed448,
|
|
1296
|
-
[-8]: JwaSignatureAlgorithm.Ed25519,
|
|
1297
|
-
[-9]: JwaSignatureAlgorithm.ES256,
|
|
1298
|
-
[-51]: JwaSignatureAlgorithm.ES384,
|
|
1299
|
-
[-52]: JwaSignatureAlgorithm.ES512,
|
|
1300
|
-
[-47]: JwaSignatureAlgorithm.ES256K,
|
|
1301
|
-
[-7]: JwaSignatureAlgorithm.ES256,
|
|
1302
|
-
[-35]: JwaSignatureAlgorithm.ES384,
|
|
1303
|
-
[-36]: JwaSignatureAlgorithm.ES512,
|
|
1304
|
-
[-257]: JwaSignatureAlgorithm.RS256,
|
|
1305
|
-
[-258]: JwaSignatureAlgorithm.RS384,
|
|
1306
|
-
[-259]: JwaSignatureAlgorithm.RS512,
|
|
1307
|
-
[-37]: JwaSignatureAlgorithm.PS256,
|
|
1308
|
-
[-38]: JwaSignatureAlgorithm.PS384,
|
|
1309
|
-
[-39]: JwaSignatureAlgorithm.PS512
|
|
1310
|
-
};
|
|
1311
|
-
/**
|
|
1312
|
-
* Transform a JWA signature algorithm identifier to an RFC 9864 fully-specified COSE algorithm identifier
|
|
1313
|
-
*
|
|
1314
|
-
* @param jwaAlg - JWA signature algorithm identifier (e.g., 'Ed25519', 'ES256')
|
|
1315
|
-
* @returns Fully-specified COSE algorithm identifier (e.g., -19, -9) or undefined if not mappable
|
|
1316
|
-
*
|
|
1317
|
-
* @example
|
|
1318
|
-
* ```typescript
|
|
1319
|
-
* const coseAlg = jwaSignatureAlgorithmToFullySpecifiedCoseAlgorithm('Ed25519') // Returns -19
|
|
1320
|
-
* const coseAlg = jwaSignatureAlgorithmToFullySpecifiedCoseAlgorithm('ES256') // Returns -9 (ESP256)
|
|
1321
|
-
* ```
|
|
1322
|
-
*/
|
|
1323
|
-
function jwaSignatureAlgorithmToFullySpecifiedCoseAlgorithm(jwaAlg) {
|
|
1324
|
-
return JWA_SIGNATURE_TO_COSE_ALGORITHM_MAP[jwaAlg];
|
|
1325
|
-
}
|
|
1326
|
-
/**
|
|
1327
|
-
* Transform a COSE algorithm identifier (either RFC 9864 fully-specified, or polymorphic) to a JWA signature algorithm identifier
|
|
1328
|
-
*
|
|
1329
|
-
* @param coseAlg - COSE algorithm identifier (e.g., -19, -9)
|
|
1330
|
-
* @returns JWA signature algorithm identifier (e.g., 'Ed25519', 'ES256') or undefined if not mappable
|
|
1331
|
-
*
|
|
1332
|
-
* @example
|
|
1333
|
-
* ```typescript
|
|
1334
|
-
* const jwaAlg = fullySpecifiedCoseAlgorithmToJwaSignatureAlgorithm(-19) // Returns 'Ed25519'
|
|
1335
|
-
* const jwaAlg = fullySpecifiedCoseAlgorithmToJwaSignatureAlgorithm(-9) // Returns 'ES256'
|
|
1336
|
-
* const jwaAlg = fullySpecifiedCoseAlgorithmToJwaSignatureAlgorithm(-7) // Returns 'ES256' (deprecated polymorphic COSE ES256)
|
|
1337
|
-
* ```
|
|
1338
|
-
*/
|
|
1339
|
-
function fullySpecifiedCoseAlgorithmToJwaSignatureAlgorithm(coseAlg) {
|
|
1340
|
-
return COSE_TO_JWA_SIGNATURE_ALGORITHM_MAP[coseAlg];
|
|
1341
|
-
}
|
|
1342
|
-
/**
|
|
1343
|
-
* Transform an array of JWA signature algorithm identifiers to RFC 9864 fully-specified COSE algorithm identifiers.
|
|
1344
|
-
*
|
|
1345
|
-
* By default it filters out unmappable algorithms. You can also choose to throw an error when an unknown
|
|
1346
|
-
* algorithm is detected.
|
|
1347
|
-
*
|
|
1348
|
-
* @param jwaAlgs - Array of JWA signature algorithm identifiers
|
|
1349
|
-
* @returns Array of fully-specified COSE algorithm identifiers
|
|
1350
|
-
*
|
|
1351
|
-
* @example
|
|
1352
|
-
* ```typescript
|
|
1353
|
-
* const coseAlgs = jwaSignatureAlgorithmArrayToFullySpecifiedCoseAlgorithmArray(['Ed25519', 'ES256', 'Unknown'])
|
|
1354
|
-
* // Returns [-19, -9]
|
|
1355
|
-
* ```
|
|
1356
|
-
*/
|
|
1357
|
-
function jwaSignatureAlgorithmArrayToFullySpecifiedCoseAlgorithmArray(jwaAlgs, throwOnUnknownValue = false) {
|
|
1358
|
-
return jwaAlgs.map((jwaAlg) => {
|
|
1359
|
-
const coseAlg = jwaSignatureAlgorithmToFullySpecifiedCoseAlgorithm(jwaAlg);
|
|
1360
|
-
if (coseAlg || !throwOnUnknownValue) return coseAlg;
|
|
1361
|
-
throw new Oauth2Error(`Found unknown JWA signature algorithm '${jwaAlg}'. Unable to map to COSE algorithm.`);
|
|
1362
|
-
}).filter((coseAlg) => coseAlg !== void 0);
|
|
1363
|
-
}
|
|
1364
|
-
/**
|
|
1365
|
-
* Transform an array of COSE algorithm identifiers (either RFC 9864 fully-specified or polymorphic) to JWA signature algorithm identifiers
|
|
1366
|
-
*
|
|
1367
|
-
* By default it filters out unmappable algorithms. You can also choose to throw an error when an unknown
|
|
1368
|
-
* algorithm is detected.
|
|
1369
|
-
*
|
|
1370
|
-
* @param coseAlgs - Array of COSE algorithm identifiers
|
|
1371
|
-
* @returns Array of JWA signature algorithm identifiers
|
|
1372
|
-
*
|
|
1373
|
-
* @example
|
|
1374
|
-
* ```typescript
|
|
1375
|
-
* const jwaAlgs = fullySpecifiedCoseAlgorithmArrayToJwaSignatureAlgorithmArray([-19, -9, 999])
|
|
1376
|
-
* // Returns ['Ed25519', 'ES256']
|
|
1377
|
-
* ```
|
|
1378
|
-
*/
|
|
1379
|
-
function fullySpecifiedCoseAlgorithmArrayToJwaSignatureAlgorithmArray(coseAlgs, throwOnUnknownValue = false) {
|
|
1380
|
-
return coseAlgs.map((coseAlg) => {
|
|
1381
|
-
const jwaAlg = fullySpecifiedCoseAlgorithmToJwaSignatureAlgorithm(coseAlg);
|
|
1382
|
-
if (jwaAlg || !throwOnUnknownValue) return jwaAlg;
|
|
1383
|
-
throw new Oauth2Error(`Found unknown COSE algorithm identifier '${coseAlg}'. Unable to map to JWA signature algorithm.`);
|
|
1384
|
-
}).filter((alg) => alg !== void 0);
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
//#endregion
|
|
1388
|
-
//#region src/error/Oauth2ClientErrorResponseError.ts
|
|
1389
|
-
var Oauth2ClientErrorResponseError = class extends Oauth2Error {
|
|
1390
|
-
constructor(message, errorResponse, response) {
|
|
1391
|
-
super(`${message}\n${JSON.stringify(errorResponse, null, 2)}`);
|
|
1392
|
-
this.errorResponse = errorResponse;
|
|
1393
|
-
this.response = response.clone();
|
|
1394
|
-
}
|
|
1395
|
-
};
|
|
1396
|
-
|
|
1397
|
-
//#endregion
|
|
1398
|
-
//#region src/error/Oauth2ClientAuthorizationChallengeError.ts
|
|
1399
|
-
var Oauth2ClientAuthorizationChallengeError = class extends Oauth2ClientErrorResponseError {
|
|
1400
|
-
constructor(message, errorResponse, response) {
|
|
1401
|
-
super(message, errorResponse, response);
|
|
1402
|
-
this.errorResponse = errorResponse;
|
|
1403
|
-
}
|
|
1404
|
-
};
|
|
1405
|
-
|
|
1406
|
-
//#endregion
|
|
1407
|
-
//#region src/error/Oauth2ResourceUnauthorizedError.ts
|
|
1408
|
-
var Oauth2ResourceUnauthorizedError = class Oauth2ResourceUnauthorizedError extends Oauth2Error {
|
|
1409
|
-
constructor(internalMessage, wwwAuthenticateHeaders) {
|
|
1410
|
-
super(`${internalMessage}\n${JSON.stringify(wwwAuthenticateHeaders, null, 2)}`);
|
|
1411
|
-
this.wwwAuthenticateHeaders = Array.isArray(wwwAuthenticateHeaders) ? wwwAuthenticateHeaders : [wwwAuthenticateHeaders];
|
|
1412
|
-
}
|
|
1413
|
-
static fromHeaderValue(value) {
|
|
1414
|
-
return new Oauth2ResourceUnauthorizedError(void 0, (0, __openid4vc_utils.parseWwwAuthenticateHeader)(value).map(({ scheme, payload: { error, error_description, scope, ...additionalPayload } }) => ({
|
|
1415
|
-
scheme,
|
|
1416
|
-
error: Array.isArray(error) ? error.join(",") : error ?? void 0,
|
|
1417
|
-
error_description: Array.isArray(error_description) ? error_description.join(",") : error_description ?? void 0,
|
|
1418
|
-
scope: Array.isArray(scope) ? scope.join(",") : scope ?? void 0,
|
|
1419
|
-
...additionalPayload
|
|
1420
|
-
})));
|
|
1421
|
-
}
|
|
1422
|
-
toHeaderValue() {
|
|
1423
|
-
return (0, __openid4vc_utils.encodeWwwAuthenticateHeader)(this.wwwAuthenticateHeaders.map((header) => ({
|
|
1424
|
-
scheme: header.scheme,
|
|
1425
|
-
payload: {
|
|
1426
|
-
error: header.error ?? null,
|
|
1427
|
-
error_description: header.error_description ?? null,
|
|
1428
|
-
scope: header.scope ?? null,
|
|
1429
|
-
...header.additionalPayload
|
|
1430
|
-
}
|
|
1431
|
-
})));
|
|
1432
|
-
}
|
|
1433
|
-
};
|
|
1434
|
-
|
|
1435
|
-
//#endregion
|
|
1436
|
-
//#region src/id-token/z-id-token-jwt.ts
|
|
1437
|
-
const zIdTokenJwtHeader = zod.default.object({ ...zJwtHeader.shape }).loose();
|
|
1438
|
-
const zIdTokenJwtPayload = zod.default.object({
|
|
1439
|
-
...zJwtPayload.shape,
|
|
1440
|
-
iss: zod.default.string(),
|
|
1441
|
-
sub: zod.default.string(),
|
|
1442
|
-
aud: zod.default.union([zod.default.string(), zod.default.array(zod.default.string())]),
|
|
1443
|
-
exp: __openid4vc_utils.zInteger,
|
|
1444
|
-
iat: __openid4vc_utils.zInteger,
|
|
1445
|
-
auth_time: __openid4vc_utils.zInteger.optional(),
|
|
1446
|
-
acr: zod.default.string().optional(),
|
|
1447
|
-
amr: zod.default.array(zod.default.string()).optional(),
|
|
1448
|
-
azp: zod.default.string().optional(),
|
|
1449
|
-
name: zod.default.string().optional(),
|
|
1450
|
-
given_name: zod.default.string().optional(),
|
|
1451
|
-
family_name: zod.default.string().optional(),
|
|
1452
|
-
middle_name: zod.default.string().optional(),
|
|
1453
|
-
nickname: zod.default.string().optional(),
|
|
1454
|
-
preferred_username: zod.default.string().optional(),
|
|
1455
|
-
profile: zod.default.url().optional(),
|
|
1456
|
-
picture: zod.default.url().optional(),
|
|
1457
|
-
website: zod.default.url().optional(),
|
|
1458
|
-
email: zod.default.email().optional(),
|
|
1459
|
-
email_verified: zod.default.boolean().optional(),
|
|
1460
|
-
gender: zod.default.enum(["male", "female"]).or(zod.default.string()).optional(),
|
|
1461
|
-
birthdate: zod.default.iso.date().optional(),
|
|
1462
|
-
zoneinfo: zod.default.string().optional(),
|
|
1463
|
-
locale: zod.default.string().optional(),
|
|
1464
|
-
phone_number: zod.default.string().optional(),
|
|
1465
|
-
phone_number_verified: zod.default.boolean().optional(),
|
|
1466
|
-
address: zod.default.object({
|
|
1467
|
-
formatted: zod.default.string().optional(),
|
|
1468
|
-
street_address: zod.default.string().optional(),
|
|
1469
|
-
locality: zod.default.string().optional(),
|
|
1470
|
-
region: zod.default.string().optional(),
|
|
1471
|
-
postal_code: zod.default.string().optional(),
|
|
1472
|
-
country: zod.default.string().optional()
|
|
1473
|
-
}).loose().optional(),
|
|
1474
|
-
updated_at: __openid4vc_utils.zInteger.optional()
|
|
1475
|
-
}).loose();
|
|
1476
|
-
|
|
1477
|
-
//#endregion
|
|
1478
|
-
//#region src/id-token/verify-id-token.ts
|
|
1479
|
-
/**
|
|
1480
|
-
* Verify an ID Token JWT.
|
|
1481
|
-
*/
|
|
1482
|
-
async function verifyIdTokenJwt(options) {
|
|
1483
|
-
const { header, payload } = decodeJwt({
|
|
1484
|
-
jwt: options.idToken,
|
|
1485
|
-
headerSchema: zIdTokenJwtHeader,
|
|
1486
|
-
payloadSchema: zIdTokenJwtPayload
|
|
1487
|
-
});
|
|
1488
|
-
const jwksUrl = options.authorizationServer.jwks_uri;
|
|
1489
|
-
if (!jwksUrl) throw new Oauth2Error(`Authorization server '${options.authorizationServer.issuer}' does not have a 'jwks_uri' parameter to fetch JWKs.`);
|
|
1490
|
-
if (payload.iss !== options.authorizationServer.issuer) throw new Oauth2Error(`Invalid 'iss' claim in id token jwt. Expected '${options.authorizationServer.issuer}', got '${payload.iss}'.`);
|
|
1491
|
-
if (payload.azp && payload.azp !== options.clientId) throw new Oauth2Error(`Invalid 'azp' claim in id token jwt. Expected '${options.clientId}', got '${payload.azp}'.`);
|
|
1492
|
-
const jwks = await fetchJwks(jwksUrl, options.callbacks.fetch);
|
|
1493
|
-
const publicJwk = extractJwkFromJwksForJwt({
|
|
1494
|
-
kid: header.kid,
|
|
1495
|
-
jwks,
|
|
1496
|
-
use: "sig"
|
|
1497
|
-
});
|
|
1498
|
-
await verifyJwt({
|
|
1499
|
-
compact: options.idToken,
|
|
1500
|
-
header,
|
|
1501
|
-
payload,
|
|
1502
|
-
signer: {
|
|
1503
|
-
method: "jwk",
|
|
1504
|
-
publicJwk,
|
|
1505
|
-
alg: header.alg
|
|
1506
|
-
},
|
|
1507
|
-
verifyJwtCallback: options.callbacks.verifyJwt,
|
|
1508
|
-
errorMessage: "Error during verification of id token jwt.",
|
|
1509
|
-
now: options.now,
|
|
1510
|
-
expectedAudience: options.clientId,
|
|
1511
|
-
expectedIssuer: options.authorizationServer.issuer,
|
|
1512
|
-
expectedNonce: options.expectedNonce
|
|
1513
|
-
});
|
|
1514
|
-
return {
|
|
1515
|
-
header,
|
|
1516
|
-
payload
|
|
1517
|
-
};
|
|
1518
|
-
}
|
|
1519
|
-
|
|
1520
|
-
//#endregion
|
|
1521
|
-
//#region src/jar/create-jar-authorization-request.ts
|
|
1522
|
-
/**
|
|
1523
|
-
* Creates a JAR (JWT Authorization Request) request object.
|
|
1524
|
-
*
|
|
1525
|
-
* @param options - The input parameters
|
|
1526
|
-
* @param options.authorizationRequestPayload - The authorization request parameters
|
|
1527
|
-
* @param options.jwtSigner - The JWT signer
|
|
1528
|
-
* @param options.jweEncryptor - The JWE encryptor (optional) if provided, the request object will be encrypted
|
|
1529
|
-
* @param options.requestUri - The request URI (optional) if provided, the request object needs to be fetched from the URI
|
|
1530
|
-
* @param options.callbacks - The callback context
|
|
1531
|
-
* @returns the requestParams, signerJwk, encryptionJwk, and requestObjectJwt
|
|
1532
|
-
*/
|
|
1533
|
-
async function createJarAuthorizationRequest(options) {
|
|
1534
|
-
const { jwtSigner, jweEncryptor, authorizationRequestPayload, requestUri, callbacks } = options;
|
|
1535
|
-
let authorizationRequestJwt;
|
|
1536
|
-
let encryptionJwk;
|
|
1537
|
-
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
1538
|
-
const { jwt, signerJwk } = await callbacks.signJwt(jwtSigner, {
|
|
1539
|
-
header: {
|
|
1540
|
-
...jwtHeaderFromJwtSigner(jwtSigner),
|
|
1541
|
-
typ: "oauth-authz-req+jwt"
|
|
1542
|
-
},
|
|
1543
|
-
payload: {
|
|
1544
|
-
iat: (0, __openid4vc_utils.dateToSeconds)(now),
|
|
1545
|
-
exp: (0, __openid4vc_utils.dateToSeconds)((0, __openid4vc_utils.addSecondsToDate)(now, options.expiresInSeconds)),
|
|
1546
|
-
...options.additionalJwtPayload,
|
|
1547
|
-
...authorizationRequestPayload
|
|
1548
|
-
}
|
|
1549
|
-
});
|
|
1550
|
-
authorizationRequestJwt = jwt;
|
|
1551
|
-
if (jweEncryptor) {
|
|
1552
|
-
const encryptionResult = await callbacks.encryptJwe(jweEncryptor, authorizationRequestJwt);
|
|
1553
|
-
authorizationRequestJwt = encryptionResult.jwe;
|
|
1554
|
-
encryptionJwk = encryptionResult.encryptionJwk;
|
|
1555
|
-
}
|
|
1556
|
-
const client_id = authorizationRequestPayload.client_id;
|
|
1557
|
-
return {
|
|
1558
|
-
jarAuthorizationRequest: requestUri ? {
|
|
1559
|
-
client_id,
|
|
1560
|
-
request_uri: requestUri
|
|
1561
|
-
} : {
|
|
1562
|
-
client_id,
|
|
1563
|
-
request: authorizationRequestJwt
|
|
1564
|
-
},
|
|
1565
|
-
signerJwk,
|
|
1566
|
-
encryptionJwk,
|
|
1567
|
-
authorizationRequestJwt
|
|
1568
|
-
};
|
|
1569
|
-
}
|
|
1570
|
-
|
|
1571
|
-
//#endregion
|
|
1572
|
-
//#region src/metadata/fetch-well-known-metadata.ts
|
|
1573
|
-
/**
|
|
1574
|
-
* Fetch well known metadata and validate the response.
|
|
1575
|
-
*
|
|
1576
|
-
* Returns null if 404 is returned
|
|
1577
|
-
* Returns validated metadata if successful response
|
|
1578
|
-
* Throws error otherwise
|
|
1579
|
-
*
|
|
1580
|
-
* @throws {ValidationError} if successful response but validation of response failed
|
|
1581
|
-
* @throws {InvalidFetchResponseError} if no successful or 404 response
|
|
1582
|
-
* @throws {Error} if parsing json from response fails
|
|
1583
|
-
*/
|
|
1584
|
-
async function fetchWellKnownMetadata(wellKnownMetadataUrl, schema, options) {
|
|
1585
|
-
const { result, response } = await (0, __openid4vc_utils.createZodFetcher)(options?.fetch)(schema, options?.acceptedContentType ?? [__openid4vc_utils.ContentType.Json], wellKnownMetadataUrl);
|
|
1586
|
-
if (response.status === 404) return null;
|
|
1587
|
-
if (!response.ok) throw new __openid4vc_utils.InvalidFetchResponseError(`Fetching well known metadata from '${wellKnownMetadataUrl}' resulted in an unsuccessful response with status '${response.status}'.`, await response.clone().text(), response);
|
|
1588
|
-
if (!result?.success) throw new ValidationError$1(`Validation of metadata from '${wellKnownMetadataUrl}' failed`, result?.error);
|
|
1589
|
-
return result.data;
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
//#endregion
|
|
1593
|
-
//#region src/metadata/authorization-server/z-authorization-server-metadata.ts
|
|
1594
|
-
const knownClientAuthenticationMethod = zod.default.enum([
|
|
1595
|
-
"client_secret_basic",
|
|
1596
|
-
"client_secret_post",
|
|
1597
|
-
"attest_jwt_client_auth",
|
|
1598
|
-
"client_secret_jwt",
|
|
1599
|
-
"private_key_jwt"
|
|
1600
|
-
]);
|
|
1601
|
-
const zAuthorizationServerMetadata = zod.default.object({
|
|
1602
|
-
issuer: __openid4vc_utils.zHttpsUrl,
|
|
1603
|
-
token_endpoint: __openid4vc_utils.zHttpsUrl,
|
|
1604
|
-
token_endpoint_auth_methods_supported: zod.default.optional(zod.default.array(zod.default.union([knownClientAuthenticationMethod, zod.default.string()]))),
|
|
1605
|
-
authorization_endpoint: zod.default.optional(__openid4vc_utils.zHttpsUrl),
|
|
1606
|
-
jwks_uri: zod.default.optional(__openid4vc_utils.zHttpsUrl),
|
|
1607
|
-
grant_types_supported: zod.default.optional(zod.default.array(zod.default.string())),
|
|
1608
|
-
code_challenge_methods_supported: zod.default.optional(zod.default.array(zod.default.string())),
|
|
1609
|
-
dpop_signing_alg_values_supported: zod.default.optional(zod.default.array(zod.default.string())),
|
|
1610
|
-
require_pushed_authorization_requests: zod.default.optional(zod.default.boolean()),
|
|
1611
|
-
pushed_authorization_request_endpoint: zod.default.optional(__openid4vc_utils.zHttpsUrl),
|
|
1612
|
-
introspection_endpoint: zod.default.optional(__openid4vc_utils.zHttpsUrl),
|
|
1613
|
-
introspection_endpoint_auth_methods_supported: zod.default.optional(zod.default.array(zod.default.union([knownClientAuthenticationMethod, zod.default.string()]))),
|
|
1614
|
-
introspection_endpoint_auth_signing_alg_values_supported: zod.default.optional(zod.default.array(zAlgValueNotNone)),
|
|
1615
|
-
authorization_challenge_endpoint: zod.default.optional(__openid4vc_utils.zHttpsUrl),
|
|
1616
|
-
"pre-authorized_grant_anonymous_access_supported": zod.default.optional(zod.default.boolean()),
|
|
1617
|
-
client_attestation_pop_nonce_required: zod.default.boolean().optional()
|
|
1618
|
-
}).loose().refine(({ introspection_endpoint_auth_methods_supported: methodsSupported, introspection_endpoint_auth_signing_alg_values_supported: algValuesSupported }) => {
|
|
1619
|
-
if (!methodsSupported) return true;
|
|
1620
|
-
if (!methodsSupported.includes("private_key_jwt") && !methodsSupported.includes("client_secret_jwt")) return true;
|
|
1621
|
-
return algValuesSupported !== void 0 && algValuesSupported.length > 0;
|
|
1622
|
-
}, `Metadata value 'introspection_endpoint_auth_signing_alg_values_supported' must be defined if metadata 'introspection_endpoint_auth_methods_supported' value contains values 'private_key_jwt' or 'client_secret_jwt'`);
|
|
1623
|
-
|
|
1624
|
-
//#endregion
|
|
1625
|
-
//#region src/metadata/authorization-server/authorization-server-metadata.ts
|
|
1626
|
-
const wellKnownAuthorizationServerSuffix = ".well-known/oauth-authorization-server";
|
|
1627
|
-
const wellKnownOpenIdConfigurationServerSuffix = ".well-known/openid-configuration";
|
|
1628
|
-
/**
|
|
1629
|
-
* fetch authorization server metadata. It first tries to fetch the oauth-authorization-server metadata. If that returns
|
|
1630
|
-
* a 404, the openid-configuration metadata will be fetched.
|
|
1631
|
-
*/
|
|
1632
|
-
async function fetchAuthorizationServerMetadata(issuer, fetch) {
|
|
1633
|
-
const parsedIssuerUrl = new __openid4vc_utils.URL(issuer);
|
|
1634
|
-
const openIdConfigurationWellKnownMetadataUrl = (0, __openid4vc_utils.joinUriParts)(issuer, [wellKnownOpenIdConfigurationServerSuffix]);
|
|
1635
|
-
const authorizationServerWellKnownMetadataUrl = (0, __openid4vc_utils.joinUriParts)(parsedIssuerUrl.origin, [wellKnownAuthorizationServerSuffix, parsedIssuerUrl.pathname]);
|
|
1636
|
-
const nonCompliantAuthorizationServerWellKnownMetadataUrl = (0, __openid4vc_utils.joinUriParts)(issuer, [wellKnownAuthorizationServerSuffix]);
|
|
1637
|
-
let authorizationServerResult = await fetchWellKnownMetadata(authorizationServerWellKnownMetadataUrl, zAuthorizationServerMetadata, { fetch });
|
|
1638
|
-
if (!authorizationServerResult && nonCompliantAuthorizationServerWellKnownMetadataUrl !== authorizationServerWellKnownMetadataUrl) authorizationServerResult = await fetchWellKnownMetadata(nonCompliantAuthorizationServerWellKnownMetadataUrl, zAuthorizationServerMetadata, { fetch });
|
|
1639
|
-
if (!authorizationServerResult) authorizationServerResult = await fetchWellKnownMetadata(openIdConfigurationWellKnownMetadataUrl, zAuthorizationServerMetadata, { fetch });
|
|
1640
|
-
if (authorizationServerResult && authorizationServerResult.issuer !== issuer) throw new Oauth2Error(`The 'issuer' parameter '${authorizationServerResult.issuer}' in the well known authorization server metadata at '${authorizationServerWellKnownMetadataUrl}' does not match the provided issuer '${issuer}'.`);
|
|
1641
|
-
return authorizationServerResult;
|
|
1642
|
-
}
|
|
1643
|
-
function getAuthorizationServerMetadataFromList(authorizationServersMetadata, issuer) {
|
|
1644
|
-
const authorizationServerMetadata = authorizationServersMetadata.find((authorizationServerMetadata$1) => authorizationServerMetadata$1.issuer === issuer);
|
|
1645
|
-
if (!authorizationServerMetadata) throw new Oauth2Error(`Authorization server '${issuer}' not found in list of authorization servers. Available authorization servers are ${authorizationServersMetadata.map((as) => `'${as.issuer}'`).join(", ")}`);
|
|
1646
|
-
return authorizationServerMetadata;
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
//#endregion
|
|
1650
|
-
//#region src/access-token/create-access-token.ts
|
|
1651
|
-
/**
|
|
1652
|
-
* Create an oauth2 access token conformant with "JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens"
|
|
1653
|
-
* @see https://datatracker.ietf.org/doc/html/rfc9068
|
|
1654
|
-
*/
|
|
1655
|
-
async function createAccessTokenJwt(options) {
|
|
1656
|
-
const header = (0, __openid4vc_utils.parseWithErrorHandling)(zAccessTokenProfileJwtHeader, {
|
|
1657
|
-
...jwtHeaderFromJwtSigner(options.signer),
|
|
1658
|
-
typ: "at+jwt"
|
|
1659
|
-
});
|
|
1660
|
-
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
1661
|
-
const payload = (0, __openid4vc_utils.parseWithErrorHandling)(zAccessTokenProfileJwtPayload, {
|
|
1662
|
-
iat: (0, __openid4vc_utils.dateToSeconds)(now),
|
|
1663
|
-
exp: (0, __openid4vc_utils.dateToSeconds)((0, __openid4vc_utils.addSecondsToDate)(now, options.expiresInSeconds)),
|
|
1664
|
-
aud: options.audience,
|
|
1665
|
-
iss: options.authorizationServer,
|
|
1666
|
-
jti: (0, __openid4vc_utils.encodeToBase64Url)(await options.callbacks.generateRandom(32)),
|
|
1667
|
-
client_id: options.clientId,
|
|
1668
|
-
sub: options.subject,
|
|
1669
|
-
scope: options.scope,
|
|
1670
|
-
cnf: options.dpop ? { jkt: await calculateJwkThumbprint({
|
|
1671
|
-
hashAlgorithm: HashAlgorithm.Sha256,
|
|
1672
|
-
hashCallback: options.callbacks.hash,
|
|
1673
|
-
jwk: options.dpop.jwk
|
|
1674
|
-
}) } : void 0,
|
|
1675
|
-
...options.additionalPayload
|
|
1676
|
-
});
|
|
1677
|
-
const { jwt } = await options.callbacks.signJwt(options.signer, {
|
|
1678
|
-
header,
|
|
1679
|
-
payload
|
|
1680
|
-
});
|
|
1681
|
-
return { jwt };
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
//#endregion
|
|
1685
|
-
//#region src/access-token/z-access-token.ts
|
|
1686
|
-
const zAccessTokenRequest = zod.default.intersection(zod.default.object({
|
|
1687
|
-
"pre-authorized_code": zod.default.optional(zod.default.string()),
|
|
1688
|
-
code: zod.default.optional(zod.default.string()),
|
|
1689
|
-
redirect_uri: zod.default.url().optional(),
|
|
1690
|
-
refresh_token: zod.default.optional(zod.default.string()),
|
|
1691
|
-
resource: zod.default.optional(__openid4vc_utils.zHttpsUrl),
|
|
1692
|
-
code_verifier: zod.default.optional(zod.default.string()),
|
|
1693
|
-
grant_type: zod.default.union([
|
|
1694
|
-
zPreAuthorizedCodeGrantIdentifier,
|
|
1695
|
-
zAuthorizationCodeGrantIdentifier,
|
|
1696
|
-
zRefreshTokenGrantIdentifier,
|
|
1697
|
-
zod.default.string()
|
|
1698
|
-
])
|
|
1699
|
-
}).loose(), zod.default.object({
|
|
1700
|
-
tx_code: zod.default.optional(zod.default.string()),
|
|
1701
|
-
user_pin: zod.default.optional(zod.default.string())
|
|
1702
|
-
}).loose().refine(({ tx_code, user_pin }) => !tx_code || !user_pin || user_pin === tx_code, { message: `If both 'tx_code' and 'user_pin' are present they must match` }).transform(({ tx_code, user_pin, ...rest }) => {
|
|
1703
|
-
return {
|
|
1704
|
-
...rest,
|
|
1705
|
-
...tx_code ?? user_pin ? { tx_code: tx_code ?? user_pin } : {}
|
|
1706
|
-
};
|
|
1707
|
-
}));
|
|
1708
|
-
const zAccessTokenResponse = zod.default.object({
|
|
1709
|
-
access_token: zod.default.string(),
|
|
1710
|
-
token_type: zod.default.string(),
|
|
1711
|
-
expires_in: zod.default.optional(zod.default.number().int()),
|
|
1712
|
-
scope: zod.default.optional(zod.default.string()),
|
|
1713
|
-
state: zod.default.optional(zod.default.string()),
|
|
1714
|
-
refresh_token: zod.default.optional(zod.default.string()),
|
|
1715
|
-
c_nonce: zod.default.optional(zod.default.string()),
|
|
1716
|
-
c_nonce_expires_in: zod.default.optional(zod.default.number().int()),
|
|
1717
|
-
authorization_details: zod.default.array(zod.default.object({}).loose()).optional()
|
|
1718
|
-
}).loose();
|
|
1719
|
-
const zAccessTokenErrorResponse = zOauth2ErrorResponse;
|
|
1720
|
-
|
|
1721
|
-
//#endregion
|
|
1722
|
-
//#region src/access-token/create-access-token-response.ts
|
|
1723
|
-
async function createAccessTokenResponse(options) {
|
|
1724
|
-
return (0, __openid4vc_utils.parseWithErrorHandling)(zAccessTokenResponse, {
|
|
1725
|
-
access_token: options.accessToken,
|
|
1726
|
-
refresh_token: options.refreshToken,
|
|
1727
|
-
token_type: options.tokenType,
|
|
1728
|
-
expires_in: options.expiresInSeconds,
|
|
1729
|
-
c_nonce: options.cNonce,
|
|
1730
|
-
c_nonce_expires_in: options.cNonceExpiresIn,
|
|
1731
|
-
...options.additionalPayload
|
|
1732
|
-
});
|
|
1733
|
-
}
|
|
1734
|
-
|
|
1735
|
-
//#endregion
|
|
1736
|
-
//#region src/access-token/parse-access-token-request.ts
|
|
1737
|
-
/**
|
|
1738
|
-
* Parse access token request and extract the grant specific properties.
|
|
1739
|
-
*
|
|
1740
|
-
* If something goes wrong, such as the grant is not supported, missing parameters, etc,
|
|
1741
|
-
* it will throw `Oauth2ServerErrorResponseError` containing an error response object
|
|
1742
|
-
* that can be returned to the client.
|
|
1743
|
-
*/
|
|
1744
|
-
function parseAccessTokenRequest(options) {
|
|
1745
|
-
const parsedAccessTokenRequest = zAccessTokenRequest.safeParse(options.accessTokenRequest);
|
|
1746
|
-
if (!parsedAccessTokenRequest.success) throw new Oauth2ServerErrorResponseError({
|
|
1747
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
1748
|
-
error_description: `Error occurred during validation of authorization request.\n${(0, __openid4vc_utils.formatZodError)(parsedAccessTokenRequest.error)}`
|
|
1749
|
-
});
|
|
1750
|
-
const accessTokenRequest = parsedAccessTokenRequest.data;
|
|
1751
|
-
let grant;
|
|
1752
|
-
if (accessTokenRequest.grant_type === preAuthorizedCodeGrantIdentifier) {
|
|
1753
|
-
if (!accessTokenRequest["pre-authorized_code"]) throw new Oauth2ServerErrorResponseError({
|
|
1754
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
1755
|
-
error_description: `Missing required 'pre-authorized_code' for grant type '${preAuthorizedCodeGrantIdentifier}'`
|
|
1756
|
-
});
|
|
1757
|
-
grant = {
|
|
1758
|
-
grantType: preAuthorizedCodeGrantIdentifier,
|
|
1759
|
-
preAuthorizedCode: accessTokenRequest["pre-authorized_code"],
|
|
1760
|
-
txCode: accessTokenRequest.tx_code
|
|
1761
|
-
};
|
|
1762
|
-
} else if (accessTokenRequest.grant_type === authorizationCodeGrantIdentifier) {
|
|
1763
|
-
if (!accessTokenRequest.code) throw new Oauth2ServerErrorResponseError({
|
|
1764
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
1765
|
-
error_description: `Missing required 'code' for grant type '${authorizationCodeGrantIdentifier}'`
|
|
1766
|
-
});
|
|
1767
|
-
grant = {
|
|
1768
|
-
grantType: authorizationCodeGrantIdentifier,
|
|
1769
|
-
code: accessTokenRequest.code
|
|
1770
|
-
};
|
|
1771
|
-
} else if (accessTokenRequest.grant_type === refreshTokenGrantIdentifier) {
|
|
1772
|
-
if (!accessTokenRequest.refresh_token) throw new Oauth2ServerErrorResponseError({
|
|
1773
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
1774
|
-
error_description: `Missing required 'refresh_token' for grant type '${refreshTokenGrantIdentifier}'`
|
|
1775
|
-
});
|
|
1776
|
-
grant = {
|
|
1777
|
-
grantType: refreshTokenGrantIdentifier,
|
|
1778
|
-
refreshToken: accessTokenRequest.refresh_token
|
|
1779
|
-
};
|
|
1780
|
-
} else throw new Oauth2ServerErrorResponseError({
|
|
1781
|
-
error: Oauth2ErrorCodes.UnsupportedGrantType,
|
|
1782
|
-
error_description: `The grant type '${accessTokenRequest.grant_type}' is not supported`
|
|
1783
|
-
});
|
|
1784
|
-
const extractedDpopJwt = extractDpopJwtFromHeaders(options.request.headers);
|
|
1785
|
-
if (!extractedDpopJwt.valid) throw new Oauth2ServerErrorResponseError({
|
|
1786
|
-
error: Oauth2ErrorCodes.InvalidDpopProof,
|
|
1787
|
-
error_description: `Request contains a 'DPoP' header, but the value is not a valid DPoP jwt`
|
|
1788
|
-
});
|
|
1789
|
-
const extractedClientAttestationJwts = extractClientAttestationJwtsFromHeaders(options.request.headers);
|
|
1790
|
-
if (!extractedClientAttestationJwts.valid) throw new Oauth2ServerErrorResponseError({
|
|
1791
|
-
error: Oauth2ErrorCodes.InvalidClient,
|
|
1792
|
-
error_description: "Request contains client attestation header, but the values are not valid client attestation and client attestation PoP header."
|
|
1793
|
-
});
|
|
1794
|
-
const pkceCodeVerifier = accessTokenRequest.code_verifier;
|
|
1795
|
-
return {
|
|
1796
|
-
accessTokenRequest,
|
|
1797
|
-
grant,
|
|
1798
|
-
dpop: extractedDpopJwt.dpopJwt ? { jwt: extractedDpopJwt.dpopJwt } : void 0,
|
|
1799
|
-
clientAttestation: extractedClientAttestationJwts.clientAttestationHeader ? {
|
|
1800
|
-
clientAttestationJwt: extractedClientAttestationJwts.clientAttestationHeader,
|
|
1801
|
-
clientAttestationPopJwt: extractedClientAttestationJwts.clientAttestationPopHeader
|
|
1802
|
-
} : void 0,
|
|
1803
|
-
pkceCodeVerifier
|
|
1804
|
-
};
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
//#endregion
|
|
1808
|
-
//#region src/pkce.ts
|
|
1809
|
-
let PkceCodeChallengeMethod = /* @__PURE__ */ function(PkceCodeChallengeMethod$1) {
|
|
1810
|
-
PkceCodeChallengeMethod$1["Plain"] = "plain";
|
|
1811
|
-
PkceCodeChallengeMethod$1["S256"] = "S256";
|
|
1812
|
-
return PkceCodeChallengeMethod$1;
|
|
1813
|
-
}({});
|
|
1814
|
-
async function createPkce(options) {
|
|
1815
|
-
const allowedCodeChallengeMethods = options.allowedCodeChallengeMethods ?? [PkceCodeChallengeMethod.S256, PkceCodeChallengeMethod.Plain];
|
|
1816
|
-
if (allowedCodeChallengeMethods.length === 0) throw new Oauth2Error(`Unable to create PKCE code verifier. 'allowedCodeChallengeMethods' is an empty array.`);
|
|
1817
|
-
const codeChallengeMethod = allowedCodeChallengeMethods.includes(PkceCodeChallengeMethod.S256) ? PkceCodeChallengeMethod.S256 : PkceCodeChallengeMethod.Plain;
|
|
1818
|
-
const codeVerifier = options.codeVerifier ?? (0, __openid4vc_utils.encodeToBase64Url)(await options.callbacks.generateRandom(64));
|
|
1819
|
-
return {
|
|
1820
|
-
codeVerifier,
|
|
1821
|
-
codeChallenge: await calculateCodeChallenge({
|
|
1822
|
-
codeChallengeMethod,
|
|
1823
|
-
codeVerifier,
|
|
1824
|
-
hashCallback: options.callbacks.hash
|
|
1825
|
-
}),
|
|
1826
|
-
codeChallengeMethod
|
|
1827
|
-
};
|
|
1828
|
-
}
|
|
1829
|
-
async function verifyPkce(options) {
|
|
1830
|
-
const calculatedCodeChallenge = await calculateCodeChallenge({
|
|
1831
|
-
codeChallengeMethod: options.codeChallengeMethod,
|
|
1832
|
-
codeVerifier: options.codeVerifier,
|
|
1833
|
-
hashCallback: options.callbacks.hash
|
|
1834
|
-
});
|
|
1835
|
-
if (options.codeChallenge !== calculatedCodeChallenge) throw new Oauth2Error(`Derived code challenge '${calculatedCodeChallenge}' from code_verifier '${options.codeVerifier}' using code challenge method '${options.codeChallengeMethod}' does not match the expected code challenge.`);
|
|
1836
|
-
}
|
|
1837
|
-
async function calculateCodeChallenge(options) {
|
|
1838
|
-
if (options.codeChallengeMethod === PkceCodeChallengeMethod.Plain) return options.codeVerifier;
|
|
1839
|
-
if (options.codeChallengeMethod === PkceCodeChallengeMethod.S256) return (0, __openid4vc_utils.encodeToBase64Url)(await options.hashCallback((0, __openid4vc_utils.decodeUtf8String)(options.codeVerifier), HashAlgorithm.Sha256));
|
|
1840
|
-
throw new Oauth2Error(`Unsupported code challenge method ${options.codeChallengeMethod}`);
|
|
1841
|
-
}
|
|
1842
|
-
|
|
1843
|
-
//#endregion
|
|
1844
|
-
//#region src/access-token/verify-access-token-request.ts
|
|
1845
|
-
async function verifyPreAuthorizedCodeAccessTokenRequest(options) {
|
|
1846
|
-
if (options.pkce) await verifyAccessTokenRequestPkce(options.pkce, options.callbacks);
|
|
1847
|
-
const dpopResult = options.dpop ? await verifyAccessTokenRequestDpop(options.dpop, options.request, options.callbacks) : void 0;
|
|
1848
|
-
const clientAttestationResult = options.clientAttestation ? await verifyAccessTokenRequestClientAttestation(options.clientAttestation, options.authorizationServerMetadata, options.callbacks, dpopResult?.jwkThumbprint, options.now) : void 0;
|
|
1849
|
-
if (options.grant.preAuthorizedCode !== options.expectedPreAuthorizedCode) throw new Oauth2ServerErrorResponseError({
|
|
1850
|
-
error: Oauth2ErrorCodes.InvalidGrant,
|
|
1851
|
-
error_description: `Invalid 'pre-authorized_code' provided`
|
|
1852
|
-
});
|
|
1853
|
-
if (options.grant.txCode !== options.expectedTxCode) {
|
|
1854
|
-
if (!options.expectedTxCode) throw new Oauth2ServerErrorResponseError({
|
|
1855
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
1856
|
-
error_description: `Request contains 'tx_code' that was not expected`
|
|
1857
|
-
});
|
|
1858
|
-
if (!options.grant.txCode) throw new Oauth2ServerErrorResponseError({
|
|
1859
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
1860
|
-
error_description: `Missing required 'tx_code' in request`
|
|
1861
|
-
});
|
|
1862
|
-
throw new Oauth2ServerErrorResponseError({
|
|
1863
|
-
error: Oauth2ErrorCodes.InvalidGrant,
|
|
1864
|
-
error_description: `Invalid 'tx_code' provided`
|
|
1865
|
-
});
|
|
1866
|
-
}
|
|
1867
|
-
if (options.preAuthorizedCodeExpiresAt) {
|
|
1868
|
-
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
1869
|
-
if (now.getTime() > options.preAuthorizedCodeExpiresAt.getTime()) throw new Oauth2ServerErrorResponseError({
|
|
1870
|
-
error: Oauth2ErrorCodes.InvalidGrant,
|
|
1871
|
-
error_description: `Expired 'pre-authorized_code' provided`
|
|
1872
|
-
}, { internalMessage: `The provided 'pre-authorized_code' in the request expired at '${options.preAuthorizedCodeExpiresAt.getTime()}', now is '${now.getTime()}'` });
|
|
1873
|
-
}
|
|
1874
|
-
return {
|
|
1875
|
-
dpop: dpopResult,
|
|
1876
|
-
clientAttestation: clientAttestationResult
|
|
1877
|
-
};
|
|
1878
|
-
}
|
|
1879
|
-
async function verifyAuthorizationCodeAccessTokenRequest(options) {
|
|
1880
|
-
if (options.pkce) await verifyAccessTokenRequestPkce(options.pkce, options.callbacks);
|
|
1881
|
-
const dpopResult = options.dpop ? await verifyAccessTokenRequestDpop(options.dpop, options.request, options.callbacks) : void 0;
|
|
1882
|
-
const clientAttestationResult = options.clientAttestation ? await verifyAccessTokenRequestClientAttestation(options.clientAttestation, options.authorizationServerMetadata, options.callbacks, dpopResult?.jwkThumbprint, options.now) : void 0;
|
|
1883
|
-
if (options.grant.code !== options.expectedCode) throw new Oauth2ServerErrorResponseError({
|
|
1884
|
-
error: Oauth2ErrorCodes.InvalidGrant,
|
|
1885
|
-
error_description: `Invalid 'code' provided`
|
|
1886
|
-
});
|
|
1887
|
-
if (options.codeExpiresAt) {
|
|
1888
|
-
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
1889
|
-
if (now.getTime() > options.codeExpiresAt.getTime()) throw new Oauth2ServerErrorResponseError({
|
|
1890
|
-
error: Oauth2ErrorCodes.InvalidGrant,
|
|
1891
|
-
error_description: `Expired 'code' provided`
|
|
1892
|
-
}, { internalMessage: `The provided 'code' in the request expired at '${options.codeExpiresAt.getTime()}', now is '${now.getTime()}'` });
|
|
1893
|
-
}
|
|
1894
|
-
return {
|
|
1895
|
-
dpop: dpopResult,
|
|
1896
|
-
clientAttestation: clientAttestationResult
|
|
1897
|
-
};
|
|
1898
|
-
}
|
|
1899
|
-
async function verifyRefreshTokenAccessTokenRequest(options) {
|
|
1900
|
-
if (options.pkce) await verifyAccessTokenRequestPkce(options.pkce, options.callbacks);
|
|
1901
|
-
const dpopResult = options.dpop ? await verifyAccessTokenRequestDpop(options.dpop, options.request, options.callbacks) : void 0;
|
|
1902
|
-
const clientAttestationResult = options.clientAttestation ? await verifyAccessTokenRequestClientAttestation(options.clientAttestation, options.authorizationServerMetadata, options.callbacks, dpopResult?.jwkThumbprint, options.now) : void 0;
|
|
1903
|
-
if (options.grant.refreshToken !== options.expectedRefreshToken) throw new Oauth2ServerErrorResponseError({
|
|
1904
|
-
error: Oauth2ErrorCodes.InvalidGrant,
|
|
1905
|
-
error_description: `Invalid 'refresh_token' provided`
|
|
1906
|
-
});
|
|
1907
|
-
if (options.refreshTokenExpiresAt) {
|
|
1908
|
-
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
1909
|
-
if (now.getTime() > options.refreshTokenExpiresAt.getTime()) throw new Oauth2ServerErrorResponseError({
|
|
1910
|
-
error: Oauth2ErrorCodes.InvalidGrant,
|
|
1911
|
-
error_description: `Expired 'refresh_token' provided`
|
|
1912
|
-
}, { internalMessage: `The provided 'refresh_token' in the request expired at '${options.refreshTokenExpiresAt.getTime()}', now is '${now.getTime()}'` });
|
|
1913
|
-
}
|
|
1914
|
-
return {
|
|
1915
|
-
dpop: dpopResult,
|
|
1916
|
-
clientAttestation: clientAttestationResult
|
|
1917
|
-
};
|
|
1918
|
-
}
|
|
1919
|
-
async function verifyAccessTokenRequestClientAttestation(options, authorizationServerMetadata, callbacks, dpopJwkThumbprint, now) {
|
|
1920
|
-
if (!options.clientAttestationJwt || !options.clientAttestationPopJwt) {
|
|
1921
|
-
if (!options.required && !options.clientAttestationJwt && !options.clientAttestationPopJwt) return;
|
|
1922
|
-
throw new Oauth2ServerErrorResponseError({
|
|
1923
|
-
error: Oauth2ErrorCodes.InvalidClient,
|
|
1924
|
-
error_description: `Missing required client attestation parameters in access token request. Make sure to provide the '${oauthClientAttestationHeader}' and '${oauthClientAttestationPopHeader}' header values.`
|
|
1925
|
-
});
|
|
1926
|
-
}
|
|
1927
|
-
const verifiedClientAttestation = await verifyClientAttestation({
|
|
1928
|
-
authorizationServer: authorizationServerMetadata.issuer,
|
|
1929
|
-
callbacks,
|
|
1930
|
-
clientAttestationJwt: options.clientAttestationJwt,
|
|
1931
|
-
clientAttestationPopJwt: options.clientAttestationPopJwt,
|
|
1932
|
-
now
|
|
1933
|
-
});
|
|
1934
|
-
if (options.expectedClientId && options.expectedClientId !== verifiedClientAttestation.clientAttestation.payload.sub) throw new Oauth2ServerErrorResponseError({
|
|
1935
|
-
error: Oauth2ErrorCodes.InvalidClient,
|
|
1936
|
-
error_description: `The client id '${verifiedClientAttestation.clientAttestation.payload.sub}' in the client attestation does not match the client id for the authorization.`
|
|
1937
|
-
}, { status: 401 });
|
|
1938
|
-
if (options.ensureConfirmationKeyMatchesDpopKey && dpopJwkThumbprint) {
|
|
1939
|
-
if (await calculateJwkThumbprint({
|
|
1940
|
-
hashAlgorithm: HashAlgorithm.Sha256,
|
|
1941
|
-
hashCallback: callbacks.hash,
|
|
1942
|
-
jwk: verifiedClientAttestation.clientAttestation.payload.cnf.jwk
|
|
1943
|
-
}) !== dpopJwkThumbprint) throw new Oauth2ServerErrorResponseError({
|
|
1944
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
1945
|
-
error_description: "Expected the DPoP JWK thumbprint value to match the JWK thumbprint of the client attestation confirmation JWK. Ensure both DPoP and client attestation use the same key."
|
|
1946
|
-
}, { status: 401 });
|
|
1947
|
-
}
|
|
1948
|
-
return verifiedClientAttestation;
|
|
1949
|
-
}
|
|
1950
|
-
async function verifyAccessTokenRequestDpop(options, request, callbacks) {
|
|
1951
|
-
if (options.required && !options.jwt) throw new Oauth2ServerErrorResponseError({
|
|
1952
|
-
error: Oauth2ErrorCodes.InvalidDpopProof,
|
|
1953
|
-
error_description: "Missing required DPoP proof"
|
|
1954
|
-
});
|
|
1955
|
-
if (!options.jwt) return void 0;
|
|
1956
|
-
const { header, jwkThumbprint } = await verifyDpopJwt({
|
|
1957
|
-
callbacks,
|
|
1958
|
-
dpopJwt: options.jwt,
|
|
1959
|
-
request,
|
|
1960
|
-
allowedSigningAlgs: options.allowedSigningAlgs,
|
|
1961
|
-
expectedJwkThumbprint: options.expectedJwkThumbprint
|
|
1962
|
-
});
|
|
1963
|
-
return {
|
|
1964
|
-
jwk: header.jwk,
|
|
1965
|
-
jwkThumbprint
|
|
1966
|
-
};
|
|
1967
|
-
}
|
|
1968
|
-
async function verifyAccessTokenRequestPkce(options, callbacks) {
|
|
1969
|
-
if (options.codeChallenge && !options.codeVerifier) throw new Oauth2ServerErrorResponseError({
|
|
1970
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
1971
|
-
error_description: `Missing required 'code_verifier' in access token request`
|
|
1972
|
-
});
|
|
1973
|
-
if (!options.codeVerifier) return null;
|
|
1974
|
-
try {
|
|
1975
|
-
await verifyPkce({
|
|
1976
|
-
callbacks,
|
|
1977
|
-
codeChallenge: options.codeChallenge,
|
|
1978
|
-
codeChallengeMethod: options.codeChallengeMethod,
|
|
1979
|
-
codeVerifier: options.codeVerifier
|
|
1980
|
-
});
|
|
1981
|
-
} catch (error) {
|
|
1982
|
-
if (error instanceof Oauth2Error) throw new Oauth2ServerErrorResponseError({
|
|
1983
|
-
error: Oauth2ErrorCodes.InvalidGrant,
|
|
1984
|
-
error_description: error.message
|
|
1985
|
-
});
|
|
1986
|
-
throw error;
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
|
-
|
|
1990
|
-
//#endregion
|
|
1991
|
-
//#region src/authorization-challenge/z-authorization-challenge.ts
|
|
1992
|
-
const zAuthorizationChallengeRequest = zod.default.object({
|
|
1993
|
-
...zAuthorizationRequest.omit({
|
|
1994
|
-
response_type: true,
|
|
1995
|
-
client_id: true
|
|
1996
|
-
}).shape,
|
|
1997
|
-
client_id: zod.default.optional(zAuthorizationRequest.shape.client_id),
|
|
1998
|
-
auth_session: zod.default.optional(zod.default.string()),
|
|
1999
|
-
presentation_during_issuance_session: zod.default.optional(zod.default.string())
|
|
2000
|
-
}).loose();
|
|
2001
|
-
const zAuthorizationChallengeResponse = zod.default.object({ authorization_code: zod.default.string() }).loose();
|
|
2002
|
-
const zAuthorizationChallengeErrorResponse = zod.default.object({
|
|
2003
|
-
...zOauth2ErrorResponse.shape,
|
|
2004
|
-
auth_session: zod.default.optional(zod.default.string()),
|
|
2005
|
-
request_uri: zod.default.optional(zod.default.string()),
|
|
2006
|
-
expires_in: zod.default.optional(__openid4vc_utils.zInteger),
|
|
2007
|
-
presentation: zod.default.optional(zod.default.string())
|
|
2008
|
-
}).loose();
|
|
2009
|
-
|
|
2010
|
-
//#endregion
|
|
2011
|
-
//#region src/authorization-challenge/create-authorization-challenge-response.ts
|
|
2012
|
-
/**
|
|
2013
|
-
* Create an authorization challenge response
|
|
2014
|
-
*
|
|
2015
|
-
* @throws {ValidationError} if an error occurred during verification of the {@link AuthorizationChallengeResponse}
|
|
2016
|
-
*/
|
|
2017
|
-
function createAuthorizationChallengeResponse(options) {
|
|
2018
|
-
return { authorizationChallengeResponse: (0, __openid4vc_utils.parseWithErrorHandling)(zAuthorizationChallengeResponse, {
|
|
2019
|
-
...options.additionalPayload,
|
|
2020
|
-
authorization_code: options.authorizationCode
|
|
2021
|
-
}) };
|
|
2022
|
-
}
|
|
2023
|
-
/**
|
|
2024
|
-
* Create an authorization challenge error response
|
|
2025
|
-
*
|
|
2026
|
-
* @throws {ValidationError} if an error occurred during validation of the {@link AuthorizationChallengeErrorResponse}
|
|
2027
|
-
*/
|
|
2028
|
-
function createAuthorizationChallengeErrorResponse(options) {
|
|
2029
|
-
return (0, __openid4vc_utils.parseWithErrorHandling)(zAuthorizationChallengeErrorResponse, {
|
|
2030
|
-
...options.additionalPayload,
|
|
2031
|
-
error: options.error,
|
|
2032
|
-
error_description: options.errorDescription,
|
|
2033
|
-
auth_session: options.authSession,
|
|
2034
|
-
presentation: options.presentation,
|
|
2035
|
-
request_uri: options.requestUri,
|
|
2036
|
-
expires_in: options.expiresIn
|
|
2037
|
-
});
|
|
2038
|
-
}
|
|
2039
|
-
|
|
2040
|
-
//#endregion
|
|
2041
|
-
//#region src/authorization-challenge/parse-authorization-challenge-request.ts
|
|
2042
|
-
/**
|
|
2043
|
-
* Parse an authorization challenge request.
|
|
2044
|
-
*
|
|
2045
|
-
* @throws {Oauth2ServerErrorResponseError}
|
|
2046
|
-
*/
|
|
2047
|
-
function parseAuthorizationChallengeRequest(options) {
|
|
2048
|
-
const parsedAuthorizationChallengeRequest = zAuthorizationChallengeRequest.safeParse(options.authorizationChallengeRequest);
|
|
2049
|
-
if (!parsedAuthorizationChallengeRequest.success) throw new Oauth2ServerErrorResponseError({
|
|
2050
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
2051
|
-
error_description: `Error occurred during validation of authorization challenge request.\n${(0, __openid4vc_utils.formatZodError)(parsedAuthorizationChallengeRequest.error)}`
|
|
2052
|
-
});
|
|
2053
|
-
const authorizationChallengeRequest = parsedAuthorizationChallengeRequest.data;
|
|
2054
|
-
const { clientAttestation, dpop } = parseAuthorizationRequest({
|
|
2055
|
-
authorizationRequest: authorizationChallengeRequest,
|
|
2056
|
-
request: options.request
|
|
2057
|
-
});
|
|
2058
|
-
return {
|
|
2059
|
-
authorizationChallengeRequest: parsedAuthorizationChallengeRequest.data,
|
|
2060
|
-
dpop,
|
|
2061
|
-
clientAttestation
|
|
2062
|
-
};
|
|
2063
|
-
}
|
|
2064
|
-
|
|
2065
|
-
//#endregion
|
|
2066
|
-
//#region src/authorization-request/verify-authorization-request.ts
|
|
2067
|
-
async function verifyAuthorizationRequest(options) {
|
|
2068
|
-
const dpopResult = options.dpop ? await verifyAuthorizationRequestDpop(options.dpop, options.request, options.callbacks, options.now) : void 0;
|
|
2069
|
-
const clientAttestationResult = options.clientAttestation ? await verifyAuthorizationRequestClientAttestation(options.clientAttestation, options.authorizationServerMetadata, options.callbacks, dpopResult?.jwkThumbprint, options.now, options.authorizationRequest.client_id) : void 0;
|
|
2070
|
-
return {
|
|
2071
|
-
dpop: dpopResult?.jwkThumbprint ? {
|
|
2072
|
-
jwkThumbprint: dpopResult.jwkThumbprint,
|
|
2073
|
-
jwk: dpopResult.jwk
|
|
2074
|
-
} : void 0,
|
|
2075
|
-
clientAttestation: clientAttestationResult
|
|
2076
|
-
};
|
|
2077
|
-
}
|
|
2078
|
-
async function verifyAuthorizationRequestClientAttestation(options, authorizationServerMetadata, callbacks, dpopJwkThumbprint, now, requestClientId) {
|
|
2079
|
-
if (!options.clientAttestationJwt || !options.clientAttestationPopJwt) {
|
|
2080
|
-
if (!options.required && !options.clientAttestationJwt && !options.clientAttestationPopJwt) return;
|
|
2081
|
-
throw new Oauth2ServerErrorResponseError({
|
|
2082
|
-
error: Oauth2ErrorCodes.InvalidClient,
|
|
2083
|
-
error_description: `Missing required client attestation parameters in pushed authorization request. Make sure to provide the '${oauthClientAttestationHeader}' and '${oauthClientAttestationPopHeader}' header values.`
|
|
2084
|
-
});
|
|
2085
|
-
}
|
|
2086
|
-
const verifiedClientAttestation = await verifyClientAttestation({
|
|
2087
|
-
authorizationServer: authorizationServerMetadata.issuer,
|
|
2088
|
-
callbacks,
|
|
2089
|
-
clientAttestationJwt: options.clientAttestationJwt,
|
|
2090
|
-
clientAttestationPopJwt: options.clientAttestationPopJwt,
|
|
2091
|
-
now
|
|
2092
|
-
});
|
|
2093
|
-
if (requestClientId && requestClientId !== verifiedClientAttestation.clientAttestation.payload.sub) throw new Oauth2ServerErrorResponseError({
|
|
2094
|
-
error: Oauth2ErrorCodes.InvalidClient,
|
|
2095
|
-
error_description: `The client_id '${requestClientId}' in the request does not match the client id '${verifiedClientAttestation.clientAttestation.payload.sub}' in the client attestation`
|
|
2096
|
-
}, { status: 401 });
|
|
2097
|
-
if (options.ensureConfirmationKeyMatchesDpopKey && dpopJwkThumbprint) {
|
|
2098
|
-
if (await calculateJwkThumbprint({
|
|
2099
|
-
hashAlgorithm: HashAlgorithm.Sha256,
|
|
2100
|
-
hashCallback: callbacks.hash,
|
|
2101
|
-
jwk: verifiedClientAttestation.clientAttestation.payload.cnf.jwk
|
|
2102
|
-
}) !== dpopJwkThumbprint) throw new Oauth2ServerErrorResponseError({
|
|
2103
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
2104
|
-
error_description: "Expected the DPoP JWK thumbprint value to match the JWK thumbprint of the client attestation confirmation JWK. Ensure both DPoP and client attestation use the same key."
|
|
2105
|
-
}, { status: 401 });
|
|
2106
|
-
}
|
|
2107
|
-
return verifiedClientAttestation;
|
|
2108
|
-
}
|
|
2109
|
-
async function verifyAuthorizationRequestDpop(options, request, callbacks, now) {
|
|
2110
|
-
if (options.required && !options.jwt && !options.jwkThumbprint) throw new Oauth2ServerErrorResponseError({
|
|
2111
|
-
error: Oauth2ErrorCodes.InvalidDpopProof,
|
|
2112
|
-
error_description: `Missing required DPoP parameters in authorization request. Either DPoP header or 'dpop_jkt' is required.`
|
|
2113
|
-
});
|
|
2114
|
-
const verifyDpopResult = options.jwt ? await verifyDpopJwt({
|
|
2115
|
-
callbacks,
|
|
2116
|
-
dpopJwt: options.jwt,
|
|
2117
|
-
request,
|
|
2118
|
-
allowedSigningAlgs: options.allowedSigningAlgs,
|
|
2119
|
-
now
|
|
2120
|
-
}) : void 0;
|
|
2121
|
-
if (options.jwkThumbprint && verifyDpopResult && options.jwkThumbprint !== verifyDpopResult.jwkThumbprint) throw new Oauth2ServerErrorResponseError({
|
|
2122
|
-
error: Oauth2ErrorCodes.InvalidDpopProof,
|
|
2123
|
-
error_description: `DPoP jwk thumbprint does not match with 'dpop_jkt' provided in authorization request`
|
|
2124
|
-
});
|
|
2125
|
-
return {
|
|
2126
|
-
jwk: verifyDpopResult?.header.jwk,
|
|
2127
|
-
jwkThumbprint: verifyDpopResult?.jwkThumbprint ?? options.jwkThumbprint
|
|
2128
|
-
};
|
|
2129
|
-
}
|
|
2130
|
-
|
|
2131
|
-
//#endregion
|
|
2132
|
-
//#region src/authorization-challenge/verify-authorization-challenge-request.ts
|
|
2133
|
-
async function verifyAuthorizationChallengeRequest(options) {
|
|
2134
|
-
const { clientAttestation, dpop } = await verifyAuthorizationRequest({
|
|
2135
|
-
...options,
|
|
2136
|
-
authorizationRequest: options.authorizationChallengeRequest
|
|
2137
|
-
});
|
|
2138
|
-
return {
|
|
2139
|
-
dpop,
|
|
2140
|
-
clientAttestation
|
|
2141
|
-
};
|
|
2142
|
-
}
|
|
2143
|
-
|
|
2144
|
-
//#endregion
|
|
2145
|
-
//#region src/authorization-request/create-pushed-authorization-response.ts
|
|
2146
|
-
/**
|
|
2147
|
-
* Create an pushed authorization response
|
|
2148
|
-
*
|
|
2149
|
-
* @throws {ValidationError} if an error occurred during verification of the {@link PushedAuthorizationResponse}
|
|
2150
|
-
*/
|
|
2151
|
-
function createPushedAuthorizationResponse(options) {
|
|
2152
|
-
return { pushedAuthorizationResponse: (0, __openid4vc_utils.parseWithErrorHandling)(zPushedAuthorizationResponse, {
|
|
2153
|
-
...options.additionalPayload,
|
|
2154
|
-
expires_in: options.expiresInSeconds,
|
|
2155
|
-
request_uri: options.requestUri
|
|
2156
|
-
}) };
|
|
2157
|
-
}
|
|
2158
|
-
/**
|
|
2159
|
-
* Create a pushed authorization error response
|
|
2160
|
-
*
|
|
2161
|
-
* @throws {ValidationError} if an error occurred during validation of the {@link PushedAuthorizationErrorResponse}
|
|
2162
|
-
*/
|
|
2163
|
-
function createPushedAuthorizationErrorResponse(options) {
|
|
2164
|
-
return (0, __openid4vc_utils.parseWithErrorHandling)(zAccessTokenErrorResponse, {
|
|
2165
|
-
...options.additionalPayload,
|
|
2166
|
-
error: options.error,
|
|
2167
|
-
error_description: options.errorDescription
|
|
2168
|
-
});
|
|
2169
|
-
}
|
|
2170
|
-
|
|
2171
|
-
//#endregion
|
|
2172
|
-
//#region src/authorization-request/verify-pushed-authorization-request.ts
|
|
2173
|
-
async function verifyPushedAuthorizationRequest(options) {
|
|
2174
|
-
let jar;
|
|
2175
|
-
if (options.authorizationRequestJwt) jar = await verifyJarRequest({
|
|
2176
|
-
authorizationRequestJwt: options.authorizationRequestJwt.jwt,
|
|
2177
|
-
jarRequestParams: options.authorizationRequest,
|
|
2178
|
-
callbacks: options.callbacks,
|
|
2179
|
-
jwtSigner: options.authorizationRequestJwt.signer
|
|
2180
|
-
});
|
|
2181
|
-
const { clientAttestation, dpop } = await verifyAuthorizationRequest(options);
|
|
2182
|
-
return {
|
|
2183
|
-
dpop,
|
|
2184
|
-
clientAttestation,
|
|
2185
|
-
jar
|
|
2186
|
-
};
|
|
2187
|
-
}
|
|
2188
|
-
|
|
2189
|
-
//#endregion
|
|
2190
|
-
//#region src/Oauth2AuthorizationServer.ts
|
|
2191
|
-
var Oauth2AuthorizationServer = class {
|
|
2192
|
-
constructor(options) {
|
|
2193
|
-
this.options = options;
|
|
2194
|
-
}
|
|
2195
|
-
createAuthorizationServerMetadata(authorizationServerMetadata) {
|
|
2196
|
-
return (0, __openid4vc_utils.parseWithErrorHandling)(zAuthorizationServerMetadata, authorizationServerMetadata, "Error validating authorization server metadata");
|
|
2197
|
-
}
|
|
2198
|
-
/**
|
|
2199
|
-
* Parse access token request and extract the grant specific properties.
|
|
2200
|
-
*
|
|
2201
|
-
* If something goes wrong, such as the grant is not supported, missing parameters, etc,
|
|
2202
|
-
* it will throw `Oauth2ServerErrorResponseError` containing an error response object
|
|
2203
|
-
* that can be returned to the client.
|
|
2204
|
-
*/
|
|
2205
|
-
parseAccessTokenRequest(options) {
|
|
2206
|
-
return parseAccessTokenRequest(options);
|
|
2207
|
-
}
|
|
2208
|
-
verifyPreAuthorizedCodeAccessTokenRequest(options) {
|
|
2209
|
-
return verifyPreAuthorizedCodeAccessTokenRequest({
|
|
2210
|
-
...options,
|
|
2211
|
-
callbacks: this.options.callbacks
|
|
2212
|
-
});
|
|
2213
|
-
}
|
|
2214
|
-
verifyAuthorizationCodeAccessTokenRequest(options) {
|
|
2215
|
-
return verifyAuthorizationCodeAccessTokenRequest({
|
|
2216
|
-
...options,
|
|
2217
|
-
callbacks: this.options.callbacks
|
|
2218
|
-
});
|
|
2219
|
-
}
|
|
2220
|
-
verifyRefreshTokenAccessTokenRequest(options) {
|
|
2221
|
-
return verifyRefreshTokenAccessTokenRequest({
|
|
2222
|
-
...options,
|
|
2223
|
-
callbacks: this.options.callbacks
|
|
2224
|
-
});
|
|
2225
|
-
}
|
|
2226
|
-
/**
|
|
2227
|
-
* Create an access token response.
|
|
2228
|
-
*
|
|
2229
|
-
* The `sub` claim can be used to identify the resource owner is subsequent requests.
|
|
2230
|
-
* For pre-auth flow this can be the pre-authorized_code but there are no requirements
|
|
2231
|
-
* on the value.
|
|
2232
|
-
*
|
|
2233
|
-
* To generate a refresh token, set the `refreshToken` option to `true`. You can
|
|
2234
|
-
* also provide a custom refresh token string.
|
|
2235
|
-
*/
|
|
2236
|
-
async createAccessTokenResponse(options) {
|
|
2237
|
-
const { jwt: accessToken } = await createAccessTokenJwt({
|
|
2238
|
-
audience: options.audience,
|
|
2239
|
-
authorizationServer: options.authorizationServer,
|
|
2240
|
-
callbacks: this.options.callbacks,
|
|
2241
|
-
expiresInSeconds: options.expiresInSeconds,
|
|
2242
|
-
subject: options.subject,
|
|
2243
|
-
scope: options.scope,
|
|
2244
|
-
clientId: options.clientId,
|
|
2245
|
-
signer: options.signer,
|
|
2246
|
-
dpop: options.dpop,
|
|
2247
|
-
now: options.now,
|
|
2248
|
-
additionalPayload: options.additionalAccessTokenPayload
|
|
2249
|
-
});
|
|
2250
|
-
return createAccessTokenResponse({
|
|
2251
|
-
accessToken,
|
|
2252
|
-
refreshToken: typeof options.refreshToken === "string" ? options.refreshToken : options.refreshToken ? (0, __openid4vc_utils.encodeToBase64Url)(await this.options.callbacks.generateRandom(32)) : void 0,
|
|
2253
|
-
callbacks: this.options.callbacks,
|
|
2254
|
-
expiresInSeconds: options.expiresInSeconds,
|
|
2255
|
-
tokenType: options.dpop ? "DPoP" : "Bearer",
|
|
2256
|
-
cNonce: options.cNonce,
|
|
2257
|
-
cNonceExpiresIn: options.cNonceExpiresIn,
|
|
2258
|
-
additionalPayload: options.additionalAccessTokenResponsePayload
|
|
2259
|
-
});
|
|
2260
|
-
}
|
|
2261
|
-
/**
|
|
2262
|
-
* Parse a pushed authorization request
|
|
2263
|
-
*/
|
|
2264
|
-
async parsePushedAuthorizationRequest(options) {
|
|
2265
|
-
return await parsePushedAuthorizationRequest({
|
|
2266
|
-
...options,
|
|
2267
|
-
callbacks: this.options.callbacks
|
|
2268
|
-
});
|
|
2269
|
-
}
|
|
2270
|
-
/**
|
|
2271
|
-
* Verify pushed authorization request.
|
|
2272
|
-
*
|
|
2273
|
-
* Make sure to provide the `authorizationRequestJwt` if this was returned in the `parsePushedAuthorizationRequest`
|
|
2274
|
-
*/
|
|
2275
|
-
verifyPushedAuthorizationRequest(options) {
|
|
2276
|
-
return verifyPushedAuthorizationRequest({
|
|
2277
|
-
...options,
|
|
2278
|
-
callbacks: this.options.callbacks
|
|
2279
|
-
});
|
|
2280
|
-
}
|
|
2281
|
-
createPushedAuthorizationResponse(options) {
|
|
2282
|
-
return createPushedAuthorizationResponse(options);
|
|
2283
|
-
}
|
|
2284
|
-
createPushedAuthorizationErrorResponse(options) {
|
|
2285
|
-
return createPushedAuthorizationErrorResponse(options);
|
|
2286
|
-
}
|
|
2287
|
-
/**
|
|
2288
|
-
* Parse an authorization challenge request
|
|
2289
|
-
*/
|
|
2290
|
-
parseAuthorizationChallengeRequest(options) {
|
|
2291
|
-
return parseAuthorizationChallengeRequest(options);
|
|
2292
|
-
}
|
|
2293
|
-
verifyAuthorizationChallengeRequest(options) {
|
|
2294
|
-
return verifyAuthorizationChallengeRequest({
|
|
2295
|
-
...options,
|
|
2296
|
-
callbacks: this.options.callbacks
|
|
2297
|
-
});
|
|
2298
|
-
}
|
|
2299
|
-
createAuthorizationChallengeResponse(options) {
|
|
2300
|
-
return createAuthorizationChallengeResponse(options);
|
|
2301
|
-
}
|
|
2302
|
-
/**
|
|
2303
|
-
* Create an authorization challenge error response indicating presentation of credentials
|
|
2304
|
-
* using OpenID4VP is required before authorization can be granted.
|
|
2305
|
-
*
|
|
2306
|
-
* The `presentation` parameter should be an OpenID4VP authorization request url.
|
|
2307
|
-
* The `authSession` should be used to track the session
|
|
2308
|
-
*/
|
|
2309
|
-
createAuthorizationChallengePresentationErrorResponse(options) {
|
|
2310
|
-
return createAuthorizationChallengeErrorResponse({
|
|
2311
|
-
error: Oauth2ErrorCodes.InsufficientAuthorization,
|
|
2312
|
-
errorDescription: options.errorDescription,
|
|
2313
|
-
additionalPayload: options.additionalPayload,
|
|
2314
|
-
authSession: options.authSession,
|
|
2315
|
-
presentation: options.presentation
|
|
2316
|
-
});
|
|
2317
|
-
}
|
|
2318
|
-
createAuthorizationChallengeErrorResponse(options) {
|
|
2319
|
-
return createAuthorizationChallengeErrorResponse(options);
|
|
2320
|
-
}
|
|
2321
|
-
async verifyDpopJwt(options) {
|
|
2322
|
-
return verifyDpopJwt({
|
|
2323
|
-
...options,
|
|
2324
|
-
callbacks: this.options.callbacks
|
|
2325
|
-
});
|
|
2326
|
-
}
|
|
2327
|
-
async verifyClientAttestation(options) {
|
|
2328
|
-
return verifyClientAttestation({
|
|
2329
|
-
...options,
|
|
2330
|
-
callbacks: this.options.callbacks
|
|
2331
|
-
});
|
|
2332
|
-
}
|
|
2333
|
-
};
|
|
2334
|
-
|
|
2335
|
-
//#endregion
|
|
2336
|
-
//#region src/dpop/dpop-retry.ts
|
|
2337
|
-
async function authorizationServerRequestWithDpopRetry(options) {
|
|
2338
|
-
try {
|
|
2339
|
-
return await options.request(options.dpop);
|
|
2340
|
-
} catch (error) {
|
|
2341
|
-
if (options.dpop && error instanceof Oauth2ClientErrorResponseError) {
|
|
2342
|
-
const dpopRetry = shouldRetryAuthorizationServerRequestWithDPoPNonce({
|
|
2343
|
-
responseHeaders: error.response.headers,
|
|
2344
|
-
errorResponse: error.errorResponse
|
|
2345
|
-
});
|
|
2346
|
-
if (dpopRetry.retry) return options.request({
|
|
2347
|
-
...options.dpop,
|
|
2348
|
-
nonce: dpopRetry.dpopNonce
|
|
2349
|
-
});
|
|
2350
|
-
}
|
|
2351
|
-
throw error;
|
|
2352
|
-
}
|
|
2353
|
-
}
|
|
2354
|
-
function shouldRetryAuthorizationServerRequestWithDPoPNonce(options) {
|
|
2355
|
-
if (options.errorResponse.error !== "use_dpop_nonce") return { retry: false };
|
|
2356
|
-
const dpopNonce = extractDpopNonceFromHeaders(options.responseHeaders);
|
|
2357
|
-
if (!dpopNonce) throw new Oauth2Error(`Error response error contains error 'use_dpop_nonce' but the response headers do not include a valid 'DPoP-Nonce' header value.`);
|
|
2358
|
-
return {
|
|
2359
|
-
retry: true,
|
|
2360
|
-
dpopNonce
|
|
2361
|
-
};
|
|
2362
|
-
}
|
|
2363
|
-
function shouldRetryResourceRequestWithDPoPNonce(options) {
|
|
2364
|
-
if (!options.resourceUnauthorizedError.wwwAuthenticateHeaders.find((challenge) => challenge.scheme === SupportedAuthenticationScheme.DPoP && challenge.error === Oauth2ErrorCodes.UseDpopNonce)) return { retry: false };
|
|
2365
|
-
const dpopNonce = extractDpopNonceFromHeaders(options.responseHeaders);
|
|
2366
|
-
if (!dpopNonce || typeof dpopNonce !== "string") throw new Oauth2Error(`Resource request error in 'WWW-Authenticate' response header contains error 'use_dpop_nonce' but the response headers do not include a valid 'DPoP-Nonce' value.`);
|
|
2367
|
-
return {
|
|
2368
|
-
retry: true,
|
|
2369
|
-
dpopNonce
|
|
2370
|
-
};
|
|
2371
|
-
}
|
|
2372
|
-
|
|
2373
|
-
//#endregion
|
|
2374
|
-
//#region src/access-token/retrieve-access-token.ts
|
|
2375
|
-
async function retrievePreAuthorizedCodeAccessToken(options) {
|
|
2376
|
-
const request = {
|
|
2377
|
-
grant_type: preAuthorizedCodeGrantIdentifier,
|
|
2378
|
-
"pre-authorized_code": options.preAuthorizedCode,
|
|
2379
|
-
tx_code: options.txCode,
|
|
2380
|
-
resource: options.resource,
|
|
2381
|
-
...options.additionalRequestPayload
|
|
2382
|
-
};
|
|
2383
|
-
return retrieveAccessToken({
|
|
2384
|
-
authorizationServerMetadata: options.authorizationServerMetadata,
|
|
2385
|
-
request,
|
|
2386
|
-
dpop: options.dpop,
|
|
2387
|
-
callbacks: options.callbacks,
|
|
2388
|
-
resource: options.resource
|
|
2389
|
-
});
|
|
2390
|
-
}
|
|
2391
|
-
async function retrieveAuthorizationCodeAccessToken(options) {
|
|
2392
|
-
const request = {
|
|
2393
|
-
grant_type: authorizationCodeGrantIdentifier,
|
|
2394
|
-
code: options.authorizationCode,
|
|
2395
|
-
code_verifier: options.pkceCodeVerifier,
|
|
2396
|
-
redirect_uri: options.redirectUri,
|
|
2397
|
-
resource: options.resource,
|
|
2398
|
-
...options.additionalRequestPayload
|
|
2399
|
-
};
|
|
2400
|
-
return retrieveAccessToken({
|
|
2401
|
-
authorizationServerMetadata: options.authorizationServerMetadata,
|
|
2402
|
-
request,
|
|
2403
|
-
dpop: options.dpop,
|
|
2404
|
-
resource: options.resource,
|
|
2405
|
-
callbacks: options.callbacks
|
|
2406
|
-
});
|
|
2407
|
-
}
|
|
2408
|
-
async function retrieveRefreshTokenAccessToken(options) {
|
|
2409
|
-
const request = {
|
|
2410
|
-
grant_type: refreshTokenGrantIdentifier,
|
|
2411
|
-
refresh_token: options.refreshToken,
|
|
2412
|
-
resource: options.resource,
|
|
2413
|
-
...options.additionalRequestPayload
|
|
2414
|
-
};
|
|
2415
|
-
return retrieveAccessToken({
|
|
2416
|
-
authorizationServerMetadata: options.authorizationServerMetadata,
|
|
2417
|
-
request,
|
|
2418
|
-
dpop: options.dpop,
|
|
2419
|
-
callbacks: options.callbacks,
|
|
2420
|
-
resource: options.resource
|
|
2421
|
-
});
|
|
2422
|
-
}
|
|
2423
|
-
/**
|
|
2424
|
-
* Internal method
|
|
2425
|
-
*/
|
|
2426
|
-
async function retrieveAccessToken(options) {
|
|
2427
|
-
const fetchWithZod = (0, __openid4vc_utils.createZodFetcher)(options.callbacks.fetch);
|
|
2428
|
-
const accessTokenRequest = (0, __openid4vc_utils.parseWithErrorHandling)(zAccessTokenRequest, options.request, "Error validating access token request");
|
|
2429
|
-
if (accessTokenRequest.tx_code) accessTokenRequest.user_pin = accessTokenRequest.tx_code;
|
|
2430
|
-
return await authorizationServerRequestWithDpopRetry({
|
|
2431
|
-
dpop: options.dpop,
|
|
2432
|
-
request: async (dpop) => {
|
|
2433
|
-
const dpopHeaders = dpop ? await createDpopHeadersForRequest({
|
|
2434
|
-
request: {
|
|
2435
|
-
method: "POST",
|
|
2436
|
-
url: options.authorizationServerMetadata.token_endpoint
|
|
2437
|
-
},
|
|
2438
|
-
signer: dpop.signer,
|
|
2439
|
-
callbacks: options.callbacks,
|
|
2440
|
-
nonce: dpop.nonce
|
|
2441
|
-
}) : void 0;
|
|
2442
|
-
const headers = new __openid4vc_utils.Headers({
|
|
2443
|
-
"Content-Type": __openid4vc_utils.ContentType.XWwwFormUrlencoded,
|
|
2444
|
-
...dpopHeaders
|
|
2445
|
-
});
|
|
2446
|
-
await options.callbacks.clientAuthentication({
|
|
2447
|
-
url: options.authorizationServerMetadata.token_endpoint,
|
|
2448
|
-
method: "POST",
|
|
2449
|
-
authorizationServerMetadata: options.authorizationServerMetadata,
|
|
2450
|
-
body: accessTokenRequest,
|
|
2451
|
-
contentType: __openid4vc_utils.ContentType.XWwwFormUrlencoded,
|
|
2452
|
-
headers
|
|
2453
|
-
});
|
|
2454
|
-
const { response, result } = await fetchWithZod(zAccessTokenResponse, __openid4vc_utils.ContentType.Json, options.authorizationServerMetadata.token_endpoint, {
|
|
2455
|
-
body: (0, __openid4vc_utils.objectToQueryParams)(accessTokenRequest).toString(),
|
|
2456
|
-
method: "POST",
|
|
2457
|
-
headers
|
|
2458
|
-
});
|
|
2459
|
-
if (!response.ok || !result) {
|
|
2460
|
-
const tokenErrorResponse = zAccessTokenErrorResponse.safeParse(await response.clone().json().catch(() => null));
|
|
2461
|
-
if (tokenErrorResponse.success) throw new Oauth2ClientErrorResponseError(`Unable to retrieve access token from '${options.authorizationServerMetadata.token_endpoint}'. Received token error response with status ${response.status}`, tokenErrorResponse.data, response);
|
|
2462
|
-
throw new __openid4vc_utils.InvalidFetchResponseError(`Unable to retrieve access token from '${options.authorizationServerMetadata.token_endpoint}'. Received response with status ${response.status}`, await response.clone().text(), response);
|
|
2463
|
-
}
|
|
2464
|
-
if (!result.success) throw new ValidationError$1("Error validating access token response", result.error);
|
|
2465
|
-
const dpopNonce = extractDpopNonceFromHeaders(response.headers) ?? void 0;
|
|
2466
|
-
return {
|
|
2467
|
-
dpop: dpop ? {
|
|
2468
|
-
...dpop,
|
|
2469
|
-
nonce: dpopNonce
|
|
2470
|
-
} : void 0,
|
|
2471
|
-
accessTokenResponse: result.data
|
|
2472
|
-
};
|
|
2473
|
-
}
|
|
2474
|
-
});
|
|
2475
|
-
}
|
|
2476
|
-
|
|
2477
|
-
//#endregion
|
|
2478
|
-
//#region src/authorization-challenge/send-authorization-challenge.ts
|
|
2479
|
-
/**
|
|
2480
|
-
* Send an authorization challenge request.
|
|
2481
|
-
*
|
|
2482
|
-
* @throws {Oauth2ClientAuthorizationChallengeError} if the request failed and a {@link AuthorizationChallengeErrorResponse} is returned
|
|
2483
|
-
* @throws {InvalidFetchResponseError} if the request failed but no error response could be parsed
|
|
2484
|
-
* @throws {ValidationError} if a successful response was received but an error occurred during verification of the {@link AuthorizationChallengeResponse}
|
|
2485
|
-
*/
|
|
2486
|
-
async function sendAuthorizationChallengeRequest(options) {
|
|
2487
|
-
const fetchWithZod = (0, __openid4vc_utils.createZodFetcher)(options.callbacks.fetch);
|
|
2488
|
-
const authorizationServerMetadata = options.authorizationServerMetadata;
|
|
2489
|
-
const authorizationChallengeEndpoint = authorizationServerMetadata.authorization_challenge_endpoint;
|
|
2490
|
-
if (!authorizationChallengeEndpoint) throw new Oauth2Error(`Unable to send authorization challenge. Authorization server '${authorizationServerMetadata.issuer}' has no 'authorization_challenge_endpoint'`);
|
|
2491
|
-
const pkce = authorizationServerMetadata.code_challenge_methods_supported && !options.authSession ? await createPkce({
|
|
2492
|
-
allowedCodeChallengeMethods: authorizationServerMetadata.code_challenge_methods_supported,
|
|
2493
|
-
callbacks: options.callbacks,
|
|
2494
|
-
codeVerifier: options.pkceCodeVerifier
|
|
2495
|
-
}) : void 0;
|
|
2496
|
-
const authorizationChallengeRequest = (0, __openid4vc_utils.parseWithErrorHandling)(zAuthorizationChallengeRequest, {
|
|
2497
|
-
...options.additionalRequestPayload,
|
|
2498
|
-
auth_session: options.authSession,
|
|
2499
|
-
scope: options.scope,
|
|
2500
|
-
redirect_uri: options.redirectUri,
|
|
2501
|
-
resource: options.resource,
|
|
2502
|
-
state: options.state,
|
|
2503
|
-
code_challenge: pkce?.codeChallenge,
|
|
2504
|
-
code_challenge_method: pkce?.codeChallengeMethod,
|
|
2505
|
-
presentation_during_issuance_session: options.presentationDuringIssuanceSession
|
|
2506
|
-
});
|
|
2507
|
-
return authorizationServerRequestWithDpopRetry({
|
|
2508
|
-
dpop: options.dpop,
|
|
2509
|
-
request: async (dpop) => {
|
|
2510
|
-
const headers = new __openid4vc_utils.Headers({
|
|
2511
|
-
...dpop ? await createDpopHeadersForRequest({
|
|
2512
|
-
request: {
|
|
2513
|
-
method: "POST",
|
|
2514
|
-
url: authorizationChallengeEndpoint
|
|
2515
|
-
},
|
|
2516
|
-
signer: dpop.signer,
|
|
2517
|
-
callbacks: options.callbacks,
|
|
2518
|
-
nonce: dpop.nonce
|
|
2519
|
-
}) : void 0,
|
|
2520
|
-
"Content-Type": __openid4vc_utils.ContentType.XWwwFormUrlencoded
|
|
2521
|
-
});
|
|
2522
|
-
await options.callbacks.clientAuthentication({
|
|
2523
|
-
url: authorizationChallengeEndpoint,
|
|
2524
|
-
method: "POST",
|
|
2525
|
-
authorizationServerMetadata: options.authorizationServerMetadata,
|
|
2526
|
-
body: authorizationChallengeRequest,
|
|
2527
|
-
contentType: __openid4vc_utils.ContentType.XWwwFormUrlencoded,
|
|
2528
|
-
headers
|
|
2529
|
-
});
|
|
2530
|
-
const { response, result } = await fetchWithZod(zAuthorizationChallengeResponse, __openid4vc_utils.ContentType.Json, authorizationChallengeEndpoint, {
|
|
2531
|
-
method: "POST",
|
|
2532
|
-
body: (0, __openid4vc_utils.objectToQueryParams)(authorizationChallengeRequest).toString(),
|
|
2533
|
-
headers
|
|
2534
|
-
});
|
|
2535
|
-
if (!response.ok || !result) {
|
|
2536
|
-
const authorizationChallengeErrorResponse = zAuthorizationChallengeErrorResponse.safeParse(await response.clone().json().catch(() => null));
|
|
2537
|
-
if (authorizationChallengeErrorResponse.success) throw new Oauth2ClientAuthorizationChallengeError(`Error requesting authorization code from authorization challenge endpoint '${authorizationServerMetadata.authorization_challenge_endpoint}'. Received response with status ${response.status}`, authorizationChallengeErrorResponse.data, response);
|
|
2538
|
-
throw new __openid4vc_utils.InvalidFetchResponseError(`Error requesting authorization code from authorization challenge endpoint '${authorizationServerMetadata.authorization_challenge_endpoint}'. Received response with status ${response.status}`, await response.clone().text(), response);
|
|
2539
|
-
}
|
|
2540
|
-
if (!result.success) throw new __openid4vc_utils.ValidationError("Error validating authorization challenge response", result.error);
|
|
2541
|
-
const dpopNonce = extractDpopNonceFromHeaders(response.headers) ?? void 0;
|
|
2542
|
-
return {
|
|
2543
|
-
pkce,
|
|
2544
|
-
dpop: dpop ? {
|
|
2545
|
-
...dpop,
|
|
2546
|
-
nonce: dpopNonce
|
|
2547
|
-
} : void 0,
|
|
2548
|
-
authorizationChallengeResponse: result.data
|
|
2549
|
-
};
|
|
2550
|
-
}
|
|
2551
|
-
});
|
|
2552
|
-
}
|
|
2553
|
-
|
|
2554
|
-
//#endregion
|
|
2555
|
-
//#region src/authorization-request/create-authorization-request.ts
|
|
2556
|
-
/**
|
|
2557
|
-
* Create an authorization request url that can be used for authorization.
|
|
2558
|
-
*
|
|
2559
|
-
* If the authorization server supports Pushed Authorization Requests (PAR) the
|
|
2560
|
-
* request will first be pushed to the authorization request, and a reference to
|
|
2561
|
-
* the authorization request will be returned (using the 'request_uri' param).
|
|
2562
|
-
*/
|
|
2563
|
-
async function createAuthorizationRequestUrl(options) {
|
|
2564
|
-
const authorizationServerMetadata = options.authorizationServerMetadata;
|
|
2565
|
-
const pushedAuthorizationRequestEndpoint = authorizationServerMetadata.pushed_authorization_request_endpoint;
|
|
2566
|
-
if (!authorizationServerMetadata.authorization_endpoint) throw new Oauth2Error(`Unable to create authorization request url. Authorization server '${authorizationServerMetadata.issuer}' has no 'authorization_endpoint'`);
|
|
2567
|
-
const pkce = authorizationServerMetadata.code_challenge_methods_supported ? await createPkce({
|
|
2568
|
-
allowedCodeChallengeMethods: authorizationServerMetadata.code_challenge_methods_supported,
|
|
2569
|
-
callbacks: options.callbacks,
|
|
2570
|
-
codeVerifier: options.pkceCodeVerifier
|
|
2571
|
-
}) : void 0;
|
|
2572
|
-
const authorizationRequest = {
|
|
2573
|
-
...options.additionalRequestPayload,
|
|
2574
|
-
response_type: "code",
|
|
2575
|
-
client_id: options.clientId,
|
|
2576
|
-
redirect_uri: options.redirectUri,
|
|
2577
|
-
resource: options.resource,
|
|
2578
|
-
scope: options.scope,
|
|
2579
|
-
state: options.state,
|
|
2580
|
-
code_challenge: pkce?.codeChallenge,
|
|
2581
|
-
code_challenge_method: pkce?.codeChallengeMethod
|
|
2582
|
-
};
|
|
2583
|
-
let pushedAuthorizationRequest;
|
|
2584
|
-
let dpop = options.dpop;
|
|
2585
|
-
if (authorizationServerMetadata.require_pushed_authorization_requests || pushedAuthorizationRequestEndpoint) {
|
|
2586
|
-
if (!pushedAuthorizationRequestEndpoint) throw new Oauth2Error(`Authorization server '${authorizationServerMetadata.issuer}' indicated that pushed authorization requests are required, but the 'pushed_authorization_request_endpoint' is missing in the authorization server metadata.`);
|
|
2587
|
-
const { pushedAuthorizationResponse, dpopNonce } = await authorizationServerRequestWithDpopRetry({
|
|
2588
|
-
dpop: options.dpop,
|
|
2589
|
-
request: async (dpop$1) => {
|
|
2590
|
-
const dpopHeaders = dpop$1 ? await createDpopHeadersForRequest({
|
|
2591
|
-
request: {
|
|
2592
|
-
method: "POST",
|
|
2593
|
-
url: pushedAuthorizationRequestEndpoint
|
|
2594
|
-
},
|
|
2595
|
-
signer: dpop$1.signer,
|
|
2596
|
-
callbacks: options.callbacks,
|
|
2597
|
-
nonce: dpop$1.nonce
|
|
2598
|
-
}) : void 0;
|
|
2599
|
-
return await pushAuthorizationRequest({
|
|
2600
|
-
authorizationServerMetadata,
|
|
2601
|
-
authorizationRequest,
|
|
2602
|
-
pushedAuthorizationRequestEndpoint,
|
|
2603
|
-
callbacks: options.callbacks,
|
|
2604
|
-
headers: dpopHeaders
|
|
2605
|
-
});
|
|
2606
|
-
}
|
|
2607
|
-
});
|
|
2608
|
-
pushedAuthorizationRequest = {
|
|
2609
|
-
request_uri: pushedAuthorizationResponse.request_uri,
|
|
2610
|
-
client_id: authorizationRequest.client_id
|
|
2611
|
-
};
|
|
2612
|
-
if (options.dpop && dpopNonce) dpop = {
|
|
2613
|
-
...options.dpop,
|
|
2614
|
-
nonce: dpopNonce
|
|
2615
|
-
};
|
|
2616
|
-
} else if (options.dpop) authorizationRequest.dpop_jkt = await calculateJwkThumbprint({
|
|
2617
|
-
hashAlgorithm: HashAlgorithm.Sha256,
|
|
2618
|
-
hashCallback: options.callbacks.hash,
|
|
2619
|
-
jwk: options.dpop.signer.publicJwk
|
|
2620
|
-
});
|
|
2621
|
-
return {
|
|
2622
|
-
authorizationRequestUrl: `${authorizationServerMetadata.authorization_endpoint}?${(0, __openid4vc_utils.objectToQueryParams)(pushedAuthorizationRequest ?? authorizationRequest).toString()}`,
|
|
2623
|
-
pkce,
|
|
2624
|
-
dpop
|
|
2625
|
-
};
|
|
2626
|
-
}
|
|
2627
|
-
async function pushAuthorizationRequest(options) {
|
|
2628
|
-
const fetchWithZod = (0, __openid4vc_utils.createZodFetcher)(options.callbacks.fetch);
|
|
2629
|
-
if (options.authorizationRequest.request_uri) throw new Oauth2Error(`Authorization request contains 'request_uri' parameter. This is not allowed for pushed authorization requests.`);
|
|
2630
|
-
const headers = new __openid4vc_utils.Headers({
|
|
2631
|
-
...options.headers,
|
|
2632
|
-
"Content-Type": __openid4vc_utils.ContentType.XWwwFormUrlencoded
|
|
2633
|
-
});
|
|
2634
|
-
await options.callbacks.clientAuthentication({
|
|
2635
|
-
url: options.pushedAuthorizationRequestEndpoint,
|
|
2636
|
-
method: "POST",
|
|
2637
|
-
authorizationServerMetadata: options.authorizationServerMetadata,
|
|
2638
|
-
body: options.authorizationRequest,
|
|
2639
|
-
contentType: __openid4vc_utils.ContentType.XWwwFormUrlencoded,
|
|
2640
|
-
headers
|
|
2641
|
-
});
|
|
2642
|
-
const { response, result } = await fetchWithZod(zPushedAuthorizationResponse, __openid4vc_utils.ContentType.Json, options.pushedAuthorizationRequestEndpoint, {
|
|
2643
|
-
method: "POST",
|
|
2644
|
-
body: (0, __openid4vc_utils.objectToQueryParams)(options.authorizationRequest).toString(),
|
|
2645
|
-
headers
|
|
2646
|
-
});
|
|
2647
|
-
if (!response.ok || !result) {
|
|
2648
|
-
const parErrorResponse = zOauth2ErrorResponse.safeParse(await response.clone().json().catch(() => null));
|
|
2649
|
-
if (parErrorResponse.success) throw new Oauth2ClientErrorResponseError(`Unable to push authorization request to '${options.pushedAuthorizationRequestEndpoint}'. Received response with status ${response.status}`, parErrorResponse.data, response);
|
|
2650
|
-
throw new __openid4vc_utils.InvalidFetchResponseError(`Unable to push authorization request to '${options.pushedAuthorizationRequestEndpoint}'. Received response with status ${response.status}`, await response.clone().text(), response);
|
|
2651
|
-
}
|
|
2652
|
-
if (!result.success) throw new ValidationError$1("Error validating pushed authorization response", result.error);
|
|
2653
|
-
return {
|
|
2654
|
-
dpopNonce: extractDpopNonceFromHeaders(response.headers),
|
|
2655
|
-
pushedAuthorizationResponse: result.data
|
|
2656
|
-
};
|
|
2657
|
-
}
|
|
2658
|
-
|
|
2659
|
-
//#endregion
|
|
2660
|
-
//#region src/resource-request/make-resource-request.ts
|
|
2661
|
-
async function resourceRequest(options) {
|
|
2662
|
-
const dpopHeaders = options.dpop ? await createDpopHeadersForRequest({
|
|
2663
|
-
request: {
|
|
2664
|
-
url: options.url,
|
|
2665
|
-
method: options.requestOptions.method ?? "GET"
|
|
2666
|
-
},
|
|
2667
|
-
signer: options.dpop.signer,
|
|
2668
|
-
callbacks: options.callbacks,
|
|
2669
|
-
nonce: options.dpop.nonce,
|
|
2670
|
-
accessToken: options.accessToken
|
|
2671
|
-
}) : void 0;
|
|
2672
|
-
const response = await (0, __openid4vc_utils.createFetcher)(options.callbacks.fetch)(options.url, {
|
|
2673
|
-
...options.requestOptions,
|
|
2674
|
-
headers: {
|
|
2675
|
-
...options.requestOptions.headers,
|
|
2676
|
-
Authorization: `${dpopHeaders ? "DPoP" : "Bearer"} ${options.accessToken}`,
|
|
2677
|
-
...dpopHeaders
|
|
2678
|
-
}
|
|
2679
|
-
});
|
|
2680
|
-
const dpopNonce = extractDpopNonceFromHeaders(response.headers);
|
|
2681
|
-
if (response.ok) return {
|
|
2682
|
-
ok: true,
|
|
2683
|
-
response,
|
|
2684
|
-
dpop: dpopNonce ? { nonce: dpopNonce } : void 0
|
|
2685
|
-
};
|
|
2686
|
-
const wwwAuthenticateHeader = response.headers.get("WWW-Authenticate");
|
|
2687
|
-
const resourceUnauthorizedError = wwwAuthenticateHeader ? Oauth2ResourceUnauthorizedError.fromHeaderValue(wwwAuthenticateHeader) : void 0;
|
|
2688
|
-
const shouldRetryWithNonce = options.dpop?.retryWithNonce ?? true;
|
|
2689
|
-
const dpopRetry = resourceUnauthorizedError ? shouldRetryResourceRequestWithDPoPNonce({
|
|
2690
|
-
responseHeaders: response.headers,
|
|
2691
|
-
resourceUnauthorizedError
|
|
2692
|
-
}) : void 0;
|
|
2693
|
-
if (shouldRetryWithNonce && dpopRetry?.retry && options.dpop) return await resourceRequest({
|
|
2694
|
-
...options,
|
|
2695
|
-
dpop: {
|
|
2696
|
-
...options.dpop,
|
|
2697
|
-
nonce: dpopRetry.dpopNonce,
|
|
2698
|
-
retryWithNonce: false
|
|
2699
|
-
}
|
|
2700
|
-
});
|
|
2701
|
-
return {
|
|
2702
|
-
ok: false,
|
|
2703
|
-
response,
|
|
2704
|
-
dpop: dpopNonce ? { nonce: dpopNonce } : void 0,
|
|
2705
|
-
wwwAuthenticate: resourceUnauthorizedError?.wwwAuthenticateHeaders
|
|
2706
|
-
};
|
|
2707
|
-
}
|
|
2708
|
-
|
|
2709
|
-
//#endregion
|
|
2710
|
-
//#region src/Oauth2Client.ts
|
|
2711
|
-
var Oauth2Client = class {
|
|
2712
|
-
constructor(options) {
|
|
2713
|
-
this.options = options;
|
|
2714
|
-
}
|
|
2715
|
-
isDpopSupported(options) {
|
|
2716
|
-
if (!options.authorizationServerMetadata.dpop_signing_alg_values_supported || options.authorizationServerMetadata.dpop_signing_alg_values_supported.length === 0) return { supported: false };
|
|
2717
|
-
return {
|
|
2718
|
-
supported: true,
|
|
2719
|
-
dpopSigningAlgValuesSupported: options.authorizationServerMetadata.dpop_signing_alg_values_supported
|
|
2720
|
-
};
|
|
2721
|
-
}
|
|
2722
|
-
isClientAttestationSupported(options) {
|
|
2723
|
-
if (!options.authorizationServerMetadata.token_endpoint_auth_methods_supported || !options.authorizationServerMetadata.token_endpoint_auth_methods_supported.includes(SupportedClientAuthenticationMethod.ClientAttestationJwt)) return { supported: false };
|
|
2724
|
-
return { supported: true };
|
|
2725
|
-
}
|
|
2726
|
-
async fetchAuthorizationServerMetadata(issuer) {
|
|
2727
|
-
return fetchAuthorizationServerMetadata(issuer, this.options.callbacks.fetch);
|
|
2728
|
-
}
|
|
2729
|
-
/**
|
|
2730
|
-
* Initiate authorization.
|
|
2731
|
-
*
|
|
2732
|
-
* It will take the followings steps:
|
|
2733
|
-
* - if `authorization_challenge_endpoint` is defined, send an authorization challenge request
|
|
2734
|
-
* - if authorization challenge request returns a `redirect_to_web` error code with `request_uri`
|
|
2735
|
-
* then construct the authorization request url based on the `request_uri`
|
|
2736
|
-
* - if the `authorization_challenge_endpoint` is not defined, or authorization challenge request reuturns a `redirect_to_web` error code without `request_uri`
|
|
2737
|
-
* then the authorization request url will be constructed as usual (optionally using PAR).
|
|
2738
|
-
*
|
|
2739
|
-
* @throws {Oauth2ClientAuthorizationChallengeError} in case of an error response. If `error` is
|
|
2740
|
-
* `insufficient_authorization` possible extra steps can be taken.
|
|
2741
|
-
*/
|
|
2742
|
-
async initiateAuthorization(options) {
|
|
2743
|
-
const pkce = options.authorizationServerMetadata.code_challenge_methods_supported ? await createPkce({
|
|
2744
|
-
allowedCodeChallengeMethods: options.authorizationServerMetadata.code_challenge_methods_supported,
|
|
2745
|
-
callbacks: this.options.callbacks,
|
|
2746
|
-
codeVerifier: options.pkceCodeVerifier
|
|
2747
|
-
}) : void 0;
|
|
2748
|
-
if (options.authorizationServerMetadata.authorization_challenge_endpoint) try {
|
|
2749
|
-
await this.sendAuthorizationChallengeRequest({
|
|
2750
|
-
authorizationServerMetadata: options.authorizationServerMetadata,
|
|
2751
|
-
additionalRequestPayload: options.additionalRequestPayload,
|
|
2752
|
-
pkceCodeVerifier: pkce?.codeVerifier,
|
|
2753
|
-
redirectUri: options.redirectUri,
|
|
2754
|
-
scope: options.scope,
|
|
2755
|
-
resource: options.resource,
|
|
2756
|
-
dpop: options.dpop,
|
|
2757
|
-
state: options.state
|
|
2758
|
-
});
|
|
2759
|
-
} catch (error) {
|
|
2760
|
-
if (!(error instanceof Oauth2ClientAuthorizationChallengeError && error.errorResponse.error === Oauth2ErrorCodes.RedirectToWeb)) throw error;
|
|
2761
|
-
if (error.errorResponse.request_uri) {
|
|
2762
|
-
const authorizationRequestUrl = `${options.authorizationServerMetadata.authorization_endpoint}?${(0, __openid4vc_utils.objectToQueryParams)({
|
|
2763
|
-
request_uri: error.errorResponse.request_uri,
|
|
2764
|
-
client_id: options.clientId
|
|
2765
|
-
}).toString()}`;
|
|
2766
|
-
const dpopNonce = extractDpopNonceFromHeaders(error.response.headers);
|
|
2767
|
-
return {
|
|
2768
|
-
dpop: options.dpop ? {
|
|
2769
|
-
...options.dpop,
|
|
2770
|
-
nonce: dpopNonce
|
|
2771
|
-
} : void 0,
|
|
2772
|
-
authorizationRequestUrl,
|
|
2773
|
-
pkce
|
|
2774
|
-
};
|
|
2775
|
-
}
|
|
2776
|
-
}
|
|
2777
|
-
return this.createAuthorizationRequestUrl({
|
|
2778
|
-
authorizationServerMetadata: options.authorizationServerMetadata,
|
|
2779
|
-
clientId: options.clientId,
|
|
2780
|
-
additionalRequestPayload: options.additionalRequestPayload,
|
|
2781
|
-
redirectUri: options.redirectUri,
|
|
2782
|
-
scope: options.scope,
|
|
2783
|
-
pkceCodeVerifier: pkce?.codeVerifier,
|
|
2784
|
-
resource: options.resource,
|
|
2785
|
-
dpop: options.dpop,
|
|
2786
|
-
state: options.state
|
|
2787
|
-
});
|
|
2788
|
-
}
|
|
2789
|
-
sendAuthorizationChallengeRequest(options) {
|
|
2790
|
-
return sendAuthorizationChallengeRequest({
|
|
2791
|
-
...options,
|
|
2792
|
-
callbacks: this.options.callbacks
|
|
2793
|
-
});
|
|
2794
|
-
}
|
|
2795
|
-
async createAuthorizationRequestUrl(options) {
|
|
2796
|
-
return createAuthorizationRequestUrl({
|
|
2797
|
-
authorizationServerMetadata: options.authorizationServerMetadata,
|
|
2798
|
-
clientId: options.clientId,
|
|
2799
|
-
additionalRequestPayload: options.additionalRequestPayload,
|
|
2800
|
-
redirectUri: options.redirectUri,
|
|
2801
|
-
resource: options.resource,
|
|
2802
|
-
scope: options.scope,
|
|
2803
|
-
callbacks: this.options.callbacks,
|
|
2804
|
-
pkceCodeVerifier: options.pkceCodeVerifier,
|
|
2805
|
-
dpop: options.dpop,
|
|
2806
|
-
state: options.state
|
|
2807
|
-
});
|
|
2808
|
-
}
|
|
2809
|
-
async retrievePreAuthorizedCodeAccessToken({ authorizationServerMetadata, preAuthorizedCode, additionalRequestPayload, txCode, dpop, resource }) {
|
|
2810
|
-
return await retrievePreAuthorizedCodeAccessToken({
|
|
2811
|
-
authorizationServerMetadata,
|
|
2812
|
-
preAuthorizedCode,
|
|
2813
|
-
txCode,
|
|
2814
|
-
resource,
|
|
2815
|
-
additionalRequestPayload: {
|
|
2816
|
-
...additionalRequestPayload,
|
|
2817
|
-
tx_code: txCode
|
|
2818
|
-
},
|
|
2819
|
-
callbacks: this.options.callbacks,
|
|
2820
|
-
dpop
|
|
2821
|
-
});
|
|
2822
|
-
}
|
|
2823
|
-
async retrieveAuthorizationCodeAccessToken({ authorizationServerMetadata, additionalRequestPayload, authorizationCode, pkceCodeVerifier, redirectUri, resource, dpop }) {
|
|
2824
|
-
return await retrieveAuthorizationCodeAccessToken({
|
|
2825
|
-
authorizationServerMetadata,
|
|
2826
|
-
authorizationCode,
|
|
2827
|
-
pkceCodeVerifier,
|
|
2828
|
-
additionalRequestPayload,
|
|
2829
|
-
resource,
|
|
2830
|
-
callbacks: this.options.callbacks,
|
|
2831
|
-
dpop,
|
|
2832
|
-
redirectUri
|
|
2833
|
-
});
|
|
2834
|
-
}
|
|
2835
|
-
async retrieveRefreshTokenAccessToken({ authorizationServerMetadata, additionalRequestPayload, refreshToken, resource, dpop }) {
|
|
2836
|
-
return await retrieveRefreshTokenAccessToken({
|
|
2837
|
-
authorizationServerMetadata,
|
|
2838
|
-
refreshToken,
|
|
2839
|
-
additionalRequestPayload,
|
|
2840
|
-
resource,
|
|
2841
|
-
callbacks: this.options.callbacks,
|
|
2842
|
-
dpop
|
|
2843
|
-
});
|
|
2844
|
-
}
|
|
2845
|
-
async resourceRequest(options) {
|
|
2846
|
-
return resourceRequest(options);
|
|
2847
|
-
}
|
|
2848
|
-
};
|
|
2849
|
-
|
|
2850
|
-
//#endregion
|
|
2851
|
-
//#region src/Oauth2ResourceServer.ts
|
|
2852
|
-
var Oauth2ResourceServer = class {
|
|
2853
|
-
constructor(options) {
|
|
2854
|
-
this.options = options;
|
|
2855
|
-
}
|
|
2856
|
-
async verifyResourceRequest(options) {
|
|
2857
|
-
return verifyResourceRequest({
|
|
2858
|
-
callbacks: this.options.callbacks,
|
|
2859
|
-
...options
|
|
2860
|
-
});
|
|
2861
|
-
}
|
|
2862
|
-
};
|
|
2863
|
-
|
|
2864
|
-
//#endregion
|
|
2865
|
-
//#region src/access-token/z-token-introspection.ts
|
|
2866
|
-
const zTokenIntrospectionRequest = zod.default.object({
|
|
2867
|
-
token: zod.default.string(),
|
|
2868
|
-
token_type_hint: zod.default.optional(zod.default.string())
|
|
2869
|
-
}).loose();
|
|
2870
|
-
const zTokenIntrospectionResponse = zod.default.object({
|
|
2871
|
-
active: zod.default.boolean(),
|
|
2872
|
-
scope: zod.default.optional(zod.default.string()),
|
|
2873
|
-
client_id: zod.default.optional(zod.default.string()),
|
|
2874
|
-
username: zod.default.optional(zod.default.string()),
|
|
2875
|
-
token_type: zod.default.optional(zod.default.string()),
|
|
2876
|
-
exp: zod.default.optional(__openid4vc_utils.zInteger),
|
|
2877
|
-
iat: zod.default.optional(__openid4vc_utils.zInteger),
|
|
2878
|
-
nbf: zod.default.optional(__openid4vc_utils.zInteger),
|
|
2879
|
-
sub: zod.default.optional(zod.default.string()),
|
|
2880
|
-
aud: zod.default.optional(zod.default.union([zod.default.string(), zod.default.array(zod.default.string())])),
|
|
2881
|
-
iss: zod.default.optional(zod.default.string()),
|
|
2882
|
-
jti: zod.default.optional(zod.default.string()),
|
|
2883
|
-
cnf: zod.default.optional(zJwtConfirmationPayload)
|
|
2884
|
-
}).loose();
|
|
2885
|
-
|
|
2886
|
-
//#endregion
|
|
2887
|
-
//#region src/access-token/introspect-token.ts
|
|
2888
|
-
async function introspectToken(options) {
|
|
2889
|
-
const fetchWithZod = (0, __openid4vc_utils.createZodFetcher)(options.callbacks.fetch);
|
|
2890
|
-
const introspectionRequest = (0, __openid4vc_utils.parseWithErrorHandling)(zTokenIntrospectionRequest, {
|
|
2891
|
-
token: options.token,
|
|
2892
|
-
token_type_hint: options.tokenTypeHint,
|
|
2893
|
-
...options.additionalPayload
|
|
2894
|
-
});
|
|
2895
|
-
const introspectionEndpoint = options.authorizationServerMetadata.introspection_endpoint;
|
|
2896
|
-
if (!introspectionEndpoint) throw new Oauth2Error(`Missing required 'introspection_endpoint' parameter in authorization server metadata`);
|
|
2897
|
-
const headers = new __openid4vc_utils.Headers({ "Content-Type": __openid4vc_utils.ContentType.XWwwFormUrlencoded });
|
|
2898
|
-
await options.callbacks.clientAuthentication({
|
|
2899
|
-
url: introspectionEndpoint,
|
|
2900
|
-
method: "POST",
|
|
2901
|
-
authorizationServerMetadata: options.authorizationServerMetadata,
|
|
2902
|
-
body: introspectionRequest,
|
|
2903
|
-
contentType: __openid4vc_utils.ContentType.XWwwFormUrlencoded,
|
|
2904
|
-
headers
|
|
2905
|
-
});
|
|
2906
|
-
const { result, response } = await fetchWithZod(zTokenIntrospectionResponse, __openid4vc_utils.ContentType.Json, introspectionEndpoint, {
|
|
2907
|
-
body: (0, __openid4vc_utils.objectToQueryParams)(introspectionRequest).toString(),
|
|
2908
|
-
method: "POST",
|
|
2909
|
-
headers
|
|
2910
|
-
});
|
|
2911
|
-
if (!response.ok || !result?.success) throw new __openid4vc_utils.InvalidFetchResponseError(`Unable to introspect token from '${introspectionEndpoint}'. Received response with status ${response.status}`, await response.clone().text(), response);
|
|
2912
|
-
return result.data;
|
|
2913
|
-
}
|
|
2914
|
-
|
|
2915
|
-
//#endregion
|
|
2916
|
-
//#region src/resource-request/verify-resource-request.ts
|
|
2917
|
-
async function verifyResourceRequest(options) {
|
|
2918
|
-
const allowedAuthenticationSchemes = options.allowedAuthenticationSchemes ?? Object.values(SupportedAuthenticationScheme);
|
|
2919
|
-
if (allowedAuthenticationSchemes.length === 0) throw new Oauth2Error(`Emtpy array provided for 'allowedAuthenticationSchemes', provide at least one allowed authentication scheme, or remove the value to allow all supported authentication schemes`);
|
|
2920
|
-
const authorizationHeader = options.request.headers.get("Authorization");
|
|
2921
|
-
if (!authorizationHeader) throw new Oauth2ResourceUnauthorizedError(`No 'Authorization' header provided in request.`, allowedAuthenticationSchemes.map((scheme$1) => ({ scheme: scheme$1 })));
|
|
2922
|
-
const [scheme, accessToken] = authorizationHeader.split(" ", 2);
|
|
2923
|
-
if (!scheme || !accessToken) throw new Oauth2ResourceUnauthorizedError(`Malformed 'Authorization' header provided in request.`, allowedAuthenticationSchemes.map((scheme$1) => ({ scheme: scheme$1 })));
|
|
2924
|
-
if (!allowedAuthenticationSchemes.includes(scheme) || scheme !== SupportedAuthenticationScheme.Bearer && scheme !== SupportedAuthenticationScheme.DPoP) throw new Oauth2ResourceUnauthorizedError(`Provided authentication scheme '${scheme}' is not allowed. Allowed authentication schemes are ${allowedAuthenticationSchemes.map((s) => `'${s}'`).join(", ")}.`, allowedAuthenticationSchemes.map((scheme$1) => ({ scheme: scheme$1 })));
|
|
2925
|
-
const verificationResult = await verifyJwtProfileAccessToken({
|
|
2926
|
-
accessToken,
|
|
2927
|
-
callbacks: options.callbacks,
|
|
2928
|
-
authorizationServers: options.authorizationServers,
|
|
2929
|
-
resourceServer: options.resourceServer,
|
|
2930
|
-
now: options.now
|
|
2931
|
-
}).catch((error) => {
|
|
2932
|
-
if (error instanceof Oauth2JwtParseError || error instanceof __openid4vc_utils.ValidationError) return null;
|
|
2933
|
-
const errorMessage = error instanceof Oauth2Error ? error.message : "Invalid access token";
|
|
2934
|
-
throw new Oauth2ResourceUnauthorizedError(`Error occurred during verification of jwt profile access token: ${error.message}`, {
|
|
2935
|
-
scheme,
|
|
2936
|
-
error: Oauth2ErrorCodes.InvalidToken,
|
|
2937
|
-
error_description: errorMessage
|
|
2938
|
-
});
|
|
2939
|
-
});
|
|
2940
|
-
let tokenPayload = verificationResult?.payload;
|
|
2941
|
-
let authorizationServer = verificationResult?.authorizationServer;
|
|
2942
|
-
if (!tokenPayload) for (const authorizationServerMetadata of options.authorizationServers) try {
|
|
2943
|
-
tokenPayload = await introspectToken({
|
|
2944
|
-
authorizationServerMetadata,
|
|
2945
|
-
callbacks: options.callbacks,
|
|
2946
|
-
token: accessToken,
|
|
2947
|
-
tokenTypeHint: scheme
|
|
2948
|
-
});
|
|
2949
|
-
authorizationServer = authorizationServerMetadata;
|
|
2950
|
-
if (tokenPayload.active) break;
|
|
2951
|
-
} catch (_error) {}
|
|
2952
|
-
if (!tokenPayload || !authorizationServer) throw new Oauth2ResourceUnauthorizedError("Could not verify token as jwt or using token introspection.", {
|
|
2953
|
-
scheme,
|
|
2954
|
-
error: Oauth2ErrorCodes.InvalidToken,
|
|
2955
|
-
error_description: "Token is not valid"
|
|
2956
|
-
});
|
|
2957
|
-
let dpopJwk;
|
|
2958
|
-
if (scheme === SupportedAuthenticationScheme.DPoP || tokenPayload.token_type === SupportedAuthenticationScheme.DPoP || tokenPayload.cnf?.jkt) {
|
|
2959
|
-
const dpopJwtResult = extractDpopJwtFromHeaders(options.request.headers);
|
|
2960
|
-
if (!dpopJwtResult.valid) throw new Oauth2ResourceUnauthorizedError(`Request contains a 'DPoP' header, but the value is not a valid DPoP jwt.`, {
|
|
2961
|
-
scheme,
|
|
2962
|
-
error: Oauth2ErrorCodes.InvalidDpopProof,
|
|
2963
|
-
error_description: `Request contains a 'DPoP' header, but the value is not a valid DPoP jwt.`
|
|
2964
|
-
});
|
|
2965
|
-
if (!dpopJwtResult.dpopJwt) throw new Oauth2ResourceUnauthorizedError(`Request is missing required 'DPoP' header.`, {
|
|
2966
|
-
scheme,
|
|
2967
|
-
error: Oauth2ErrorCodes.InvalidDpopProof,
|
|
2968
|
-
error_description: `Request is missing required 'DPoP' header.`
|
|
2969
|
-
});
|
|
2970
|
-
if (!tokenPayload.cnf?.jkt) throw new Oauth2ResourceUnauthorizedError(`Token payload is missing required 'cnf.jkt' value for DPoP verification.`, {
|
|
2971
|
-
scheme,
|
|
2972
|
-
error: Oauth2ErrorCodes.InvalidToken,
|
|
2973
|
-
error_description: `Token payload is missing required 'cnf.jkt' value for DPoP verification.`
|
|
2974
|
-
});
|
|
2975
|
-
try {
|
|
2976
|
-
dpopJwk = (await verifyDpopJwt({
|
|
2977
|
-
callbacks: options.callbacks,
|
|
2978
|
-
dpopJwt: dpopJwtResult.dpopJwt,
|
|
2979
|
-
request: options.request,
|
|
2980
|
-
accessToken,
|
|
2981
|
-
now: options.now,
|
|
2982
|
-
expectedJwkThumbprint: tokenPayload.cnf?.jkt,
|
|
2983
|
-
allowedSigningAlgs: authorizationServer.dpop_signing_alg_values_supported
|
|
2984
|
-
})).header.jwk;
|
|
2985
|
-
} catch (error) {
|
|
2986
|
-
const errorMessage = error instanceof Oauth2Error ? error.message : "Error verifying DPoP jwt";
|
|
2987
|
-
throw new Oauth2ResourceUnauthorizedError(`Error occurred during verification of jwt profile access token: ${error instanceof Error ? error.message : error}`, {
|
|
2988
|
-
scheme,
|
|
2989
|
-
error: Oauth2ErrorCodes.InvalidDpopProof,
|
|
2990
|
-
error_description: errorMessage
|
|
2991
|
-
});
|
|
2992
|
-
}
|
|
2993
|
-
}
|
|
2994
|
-
return {
|
|
2995
|
-
tokenPayload,
|
|
2996
|
-
dpop: dpopJwk ? { jwk: dpopJwk } : void 0,
|
|
2997
|
-
scheme,
|
|
2998
|
-
accessToken,
|
|
2999
|
-
authorizationServer: authorizationServer.issuer
|
|
3000
|
-
};
|
|
3001
|
-
}
|
|
3002
|
-
|
|
3003
|
-
//#endregion
|
|
3004
|
-
exports.HashAlgorithm = HashAlgorithm;
|
|
3005
|
-
Object.defineProperty(exports, 'InvalidFetchResponseError', {
|
|
3006
|
-
enumerable: true,
|
|
3007
|
-
get: function () {
|
|
3008
|
-
return __openid4vc_utils.InvalidFetchResponseError;
|
|
3009
|
-
}
|
|
3010
|
-
});
|
|
3011
|
-
exports.Oauth2AuthorizationServer = Oauth2AuthorizationServer;
|
|
3012
|
-
exports.Oauth2Client = Oauth2Client;
|
|
3013
|
-
exports.Oauth2ClientAuthorizationChallengeError = Oauth2ClientAuthorizationChallengeError;
|
|
3014
|
-
exports.Oauth2ClientErrorResponseError = Oauth2ClientErrorResponseError;
|
|
3015
|
-
exports.Oauth2Error = Oauth2Error;
|
|
3016
|
-
exports.Oauth2ErrorCodes = Oauth2ErrorCodes;
|
|
3017
|
-
exports.Oauth2JwtParseError = Oauth2JwtParseError;
|
|
3018
|
-
exports.Oauth2JwtVerificationError = Oauth2JwtVerificationError;
|
|
3019
|
-
exports.Oauth2ResourceServer = Oauth2ResourceServer;
|
|
3020
|
-
exports.Oauth2ResourceUnauthorizedError = Oauth2ResourceUnauthorizedError;
|
|
3021
|
-
exports.Oauth2ServerErrorResponseError = Oauth2ServerErrorResponseError;
|
|
3022
|
-
exports.PkceCodeChallengeMethod = PkceCodeChallengeMethod;
|
|
3023
|
-
exports.SupportedAuthenticationScheme = SupportedAuthenticationScheme;
|
|
3024
|
-
exports.SupportedClientAuthenticationMethod = SupportedClientAuthenticationMethod;
|
|
3025
|
-
exports.authorizationCodeGrantIdentifier = authorizationCodeGrantIdentifier;
|
|
3026
|
-
exports.calculateJwkThumbprint = calculateJwkThumbprint;
|
|
3027
|
-
exports.clientAuthenticationAnonymous = clientAuthenticationAnonymous;
|
|
3028
|
-
exports.clientAuthenticationClientAttestationJwt = clientAuthenticationClientAttestationJwt;
|
|
3029
|
-
exports.clientAuthenticationClientSecretBasic = clientAuthenticationClientSecretBasic;
|
|
3030
|
-
exports.clientAuthenticationClientSecretPost = clientAuthenticationClientSecretPost;
|
|
3031
|
-
exports.clientAuthenticationDynamic = clientAuthenticationDynamic;
|
|
3032
|
-
exports.clientAuthenticationNone = clientAuthenticationNone;
|
|
3033
|
-
exports.createClientAttestationJwt = createClientAttestationJwt;
|
|
3034
|
-
exports.createJarAuthorizationRequest = createJarAuthorizationRequest;
|
|
3035
|
-
exports.decodeJwt = decodeJwt;
|
|
3036
|
-
exports.decodeJwtHeader = decodeJwtHeader;
|
|
3037
|
-
exports.fetchAuthorizationServerMetadata = fetchAuthorizationServerMetadata;
|
|
3038
|
-
exports.fetchJwks = fetchJwks;
|
|
3039
|
-
exports.fetchWellKnownMetadata = fetchWellKnownMetadata;
|
|
3040
|
-
exports.fullySpecifiedCoseAlgorithmArrayToJwaSignatureAlgorithmArray = fullySpecifiedCoseAlgorithmArrayToJwaSignatureAlgorithmArray;
|
|
3041
|
-
exports.fullySpecifiedCoseAlgorithmToJwaSignatureAlgorithm = fullySpecifiedCoseAlgorithmToJwaSignatureAlgorithm;
|
|
3042
|
-
exports.getAuthorizationServerMetadataFromList = getAuthorizationServerMetadataFromList;
|
|
3043
|
-
Object.defineProperty(exports, 'getGlobalConfig', {
|
|
3044
|
-
enumerable: true,
|
|
3045
|
-
get: function () {
|
|
3046
|
-
return __openid4vc_utils.getGlobalConfig;
|
|
3047
|
-
}
|
|
3048
|
-
});
|
|
3049
|
-
exports.isJwkInSet = isJwkInSet;
|
|
3050
|
-
exports.jwaSignatureAlgorithmArrayToFullySpecifiedCoseAlgorithmArray = jwaSignatureAlgorithmArrayToFullySpecifiedCoseAlgorithmArray;
|
|
3051
|
-
exports.jwaSignatureAlgorithmToFullySpecifiedCoseAlgorithm = jwaSignatureAlgorithmToFullySpecifiedCoseAlgorithm;
|
|
3052
|
-
exports.jwtAuthorizationRequestJwtHeaderTyp = jwtAuthorizationRequestJwtHeaderTyp;
|
|
3053
|
-
exports.jwtHeaderFromJwtSigner = jwtHeaderFromJwtSigner;
|
|
3054
|
-
exports.jwtSignerFromJwt = jwtSignerFromJwt;
|
|
3055
|
-
exports.parseAuthorizationResponseRedirectUrl = parseAuthorizationResponseRedirectUrl;
|
|
3056
|
-
exports.parsePushedAuthorizationRequestUriReferenceValue = parsePushedAuthorizationRequestUriReferenceValue;
|
|
3057
|
-
exports.preAuthorizedCodeGrantIdentifier = preAuthorizedCodeGrantIdentifier;
|
|
3058
|
-
exports.pushedAuthorizationRequestUriPrefix = pushedAuthorizationRequestUriPrefix;
|
|
3059
|
-
exports.refreshTokenGrantIdentifier = refreshTokenGrantIdentifier;
|
|
3060
|
-
exports.resourceRequest = resourceRequest;
|
|
3061
|
-
Object.defineProperty(exports, 'setGlobalConfig', {
|
|
3062
|
-
enumerable: true,
|
|
3063
|
-
get: function () {
|
|
3064
|
-
return __openid4vc_utils.setGlobalConfig;
|
|
3065
|
-
}
|
|
3066
|
-
});
|
|
3067
|
-
exports.signedAuthorizationRequestJwtHeaderTyp = signedAuthorizationRequestJwtHeaderTyp;
|
|
3068
|
-
exports.validateJarRequestParams = validateJarRequestParams;
|
|
3069
|
-
exports.verifyClientAttestationJwt = verifyClientAttestationJwt;
|
|
3070
|
-
exports.verifyIdTokenJwt = verifyIdTokenJwt;
|
|
3071
|
-
exports.verifyJwt = verifyJwt;
|
|
3072
|
-
exports.verifyResourceRequest = verifyResourceRequest;
|
|
3073
|
-
exports.zAlgValueNotNone = zAlgValueNotNone;
|
|
3074
|
-
exports.zAuthorizationCodeGrantIdentifier = zAuthorizationCodeGrantIdentifier;
|
|
3075
|
-
exports.zAuthorizationErrorResponse = zAuthorizationErrorResponse;
|
|
3076
|
-
exports.zAuthorizationResponse = zAuthorizationResponse;
|
|
3077
|
-
exports.zAuthorizationResponseFromUriParams = zAuthorizationResponseFromUriParams;
|
|
3078
|
-
exports.zAuthorizationServerMetadata = zAuthorizationServerMetadata;
|
|
3079
|
-
exports.zCompactJwe = zCompactJwe;
|
|
3080
|
-
exports.zCompactJwt = zCompactJwt;
|
|
3081
|
-
exports.zIdTokenJwtHeader = zIdTokenJwtHeader;
|
|
3082
|
-
exports.zIdTokenJwtPayload = zIdTokenJwtPayload;
|
|
3083
|
-
exports.zJarAuthorizationRequest = zJarAuthorizationRequest;
|
|
3084
|
-
exports.zJarRequestObjectPayload = zJarRequestObjectPayload;
|
|
3085
|
-
exports.zJwk = zJwk;
|
|
3086
|
-
exports.zJwkSet = zJwkSet;
|
|
3087
|
-
exports.zJwtHeader = zJwtHeader;
|
|
3088
|
-
exports.zJwtPayload = zJwtPayload;
|
|
3089
|
-
exports.zOauth2ErrorResponse = zOauth2ErrorResponse;
|
|
3090
|
-
exports.zPreAuthorizedCodeGrantIdentifier = zPreAuthorizedCodeGrantIdentifier;
|
|
3091
|
-
exports.zPushedAuthorizationRequestUriPrefix = zPushedAuthorizationRequestUriPrefix;
|
|
3092
|
-
exports.zRefreshTokenGrantIdentifier = zRefreshTokenGrantIdentifier;
|
|
3093
|
-
//# sourceMappingURL=index.cjs.map
|