@openid4vc/openid4vp 0.3.0-alpha-20250707100752 → 0.3.0-alpha-20250707121837
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 +8237 -89
- package/dist/index.d.ts +8237 -89
- package/dist/index.js +489 -306
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +362 -178
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
// src/client-identifier-
|
|
1
|
+
// src/client-identifier-prefix/parse-client-identifier-prefix.ts
|
|
2
2
|
import { Oauth2ErrorCodes, Oauth2ServerErrorResponseError } from "@openid4vc/oauth2";
|
|
3
|
-
import {
|
|
3
|
+
import { HashAlgorithm } from "@openid4vc/oauth2";
|
|
4
|
+
import { URL as URL2, decodeBase64, encodeToBase64Url, zHttpsUrl as zHttpsUrl3 } from "@openid4vc/utils";
|
|
4
5
|
|
|
5
6
|
// src/authorization-request/z-authorization-request-dc-api.ts
|
|
6
7
|
import { z as z6 } from "zod";
|
|
@@ -54,7 +55,7 @@ var zJarmClientMetadataParsed = zJarmClientMetadata.transform((client_metadata)
|
|
|
54
55
|
type: "sign_encrypt",
|
|
55
56
|
client_metadata: {
|
|
56
57
|
...SignEncrypt.data,
|
|
57
|
-
authorization_encrypted_response_enc: client_metadata.authorization_encrypted_response_enc
|
|
58
|
+
authorization_encrypted_response_enc: client_metadata.authorization_encrypted_response_enc
|
|
58
59
|
}
|
|
59
60
|
};
|
|
60
61
|
}
|
|
@@ -64,7 +65,7 @@ var zJarmClientMetadataParsed = zJarmClientMetadata.transform((client_metadata)
|
|
|
64
65
|
type: "encrypt",
|
|
65
66
|
client_metadata: {
|
|
66
67
|
...encryptOnly.data,
|
|
67
|
-
authorization_encrypted_response_enc: parsedClientMeta.authorization_encrypted_response_enc
|
|
68
|
+
authorization_encrypted_response_enc: parsedClientMeta.authorization_encrypted_response_enc
|
|
68
69
|
}
|
|
69
70
|
};
|
|
70
71
|
}
|
|
@@ -74,7 +75,7 @@ var zJarmClientMetadataParsed = zJarmClientMetadata.transform((client_metadata)
|
|
|
74
75
|
type: "sign",
|
|
75
76
|
client_metadata: {
|
|
76
77
|
...signOnly.data,
|
|
77
|
-
authorization_signed_response_alg: parsedClientMeta.authorization_signed_response_alg
|
|
78
|
+
authorization_signed_response_alg: parsedClientMeta.authorization_signed_response_alg
|
|
78
79
|
}
|
|
79
80
|
};
|
|
80
81
|
}
|
|
@@ -83,7 +84,36 @@ var zJarmClientMetadataParsed = zJarmClientMetadata.transform((client_metadata)
|
|
|
83
84
|
|
|
84
85
|
// src/models/z-vp-formats-supported.ts
|
|
85
86
|
import { z as z2 } from "zod";
|
|
86
|
-
var zVpFormatsSupported = z2.
|
|
87
|
+
var zVpFormatsSupported = z2.object({
|
|
88
|
+
"dc+sd-jwt": z2.optional(
|
|
89
|
+
z2.object({
|
|
90
|
+
"sd-jwt_alg_values": z2.optional(z2.array(z2.string()).nonempty()),
|
|
91
|
+
"kb-jwt_alg_values": z2.optional(z2.array(z2.string()).nonempty())
|
|
92
|
+
}).passthrough()
|
|
93
|
+
),
|
|
94
|
+
jwt_vc_json: z2.optional(
|
|
95
|
+
z2.object({
|
|
96
|
+
alg_values: z2.optional(z2.array(z2.string()).nonempty())
|
|
97
|
+
}).passthrough()
|
|
98
|
+
),
|
|
99
|
+
ldp_vc: z2.optional(
|
|
100
|
+
z2.object({
|
|
101
|
+
proof_type_values: z2.optional(z2.array(z2.string()).nonempty()),
|
|
102
|
+
cryptosuite_values: z2.optional(z2.array(z2.string()).nonempty())
|
|
103
|
+
}).passthrough()
|
|
104
|
+
),
|
|
105
|
+
mso_mdoc: z2.optional(
|
|
106
|
+
z2.object({
|
|
107
|
+
// Draft 27
|
|
108
|
+
issuer_signed_alg_values: z2.optional(z2.array(z2.number()).nonempty()),
|
|
109
|
+
device_signed_alg_values: z2.optional(z2.array(z2.number()).nonempty()),
|
|
110
|
+
// Draft 28+
|
|
111
|
+
issuerauth_alg_values: z2.optional(z2.array(z2.number()).nonempty()),
|
|
112
|
+
deviceauth_alg_values: z2.optional(z2.array(z2.number()).nonempty())
|
|
113
|
+
}).passthrough()
|
|
114
|
+
)
|
|
115
|
+
}).passthrough().catchall(z2.object({}).passthrough());
|
|
116
|
+
var zLegacyVpFormats = z2.record(
|
|
87
117
|
z2.string(),
|
|
88
118
|
z2.object({
|
|
89
119
|
alg_values_supported: z2.optional(z2.array(z2.string()))
|
|
@@ -95,7 +125,12 @@ var zClientMetadata = z3.object({
|
|
|
95
125
|
// Up until draft 22
|
|
96
126
|
jwks_uri: z3.string().url().optional(),
|
|
97
127
|
jwks: z3.optional(zJwkSet),
|
|
98
|
-
|
|
128
|
+
// Up until draft 26
|
|
129
|
+
vp_formats: z3.optional(zLegacyVpFormats),
|
|
130
|
+
// From draft 27
|
|
131
|
+
vp_formats_supported: z3.optional(zVpFormatsSupported),
|
|
132
|
+
// From draft 28
|
|
133
|
+
encrypted_response_enc_values_supported: z3.optional(z3.array(z3.string())),
|
|
99
134
|
...zJarmClientMetadata.shape,
|
|
100
135
|
logo_uri: zHttpsUrl.optional(),
|
|
101
136
|
client_name: z3.string().optional()
|
|
@@ -137,9 +172,11 @@ var zOpenid4vpAuthorizationRequest = z5.object({
|
|
|
137
172
|
"did",
|
|
138
173
|
"verifier_attestation",
|
|
139
174
|
"x509_san_dns",
|
|
140
|
-
"x509_san_uri"
|
|
175
|
+
"x509_san_uri",
|
|
176
|
+
"x509_hash"
|
|
141
177
|
]).optional(),
|
|
142
|
-
verifier_attestations: zVerifierAttestations.optional()
|
|
178
|
+
verifier_attestations: zVerifierAttestations.optional(),
|
|
179
|
+
verifier_info: zVerifierAttestations.optional()
|
|
143
180
|
}).passthrough();
|
|
144
181
|
var zOpenid4vpAuthorizationRequestFromUriParams = z5.string().url().transform((url) => Object.fromEntries(new URL(url).searchParams)).pipe(
|
|
145
182
|
z5.object({
|
|
@@ -147,7 +184,8 @@ var zOpenid4vpAuthorizationRequestFromUriParams = z5.string().url().transform((u
|
|
|
147
184
|
client_metadata: zStringToJson.optional(),
|
|
148
185
|
dcql_query: zStringToJson.optional(),
|
|
149
186
|
transaction_data: zStringToJson.optional(),
|
|
150
|
-
verifier_attestations: zStringToJson.optional()
|
|
187
|
+
verifier_attestations: zStringToJson.optional(),
|
|
188
|
+
verifier_info: zStringToJson.optional()
|
|
151
189
|
}).passthrough()
|
|
152
190
|
);
|
|
153
191
|
|
|
@@ -162,7 +200,8 @@ var zOpenid4vpAuthorizationRequestDcApi = zOpenid4vpAuthorizationRequest.pick({
|
|
|
162
200
|
dcql_query: true,
|
|
163
201
|
trust_chain: true,
|
|
164
202
|
state: true,
|
|
165
|
-
verifier_attestations: true
|
|
203
|
+
verifier_attestations: true,
|
|
204
|
+
verifier_info: true
|
|
166
205
|
}).extend({
|
|
167
206
|
client_id: z6.optional(z6.string()),
|
|
168
207
|
expected_origins: z6.array(z6.string()).optional(),
|
|
@@ -179,31 +218,55 @@ function isOpenid4vpAuthorizationRequestDcApi(request) {
|
|
|
179
218
|
return isOpenid4vpResponseModeDcApi(request.response_mode);
|
|
180
219
|
}
|
|
181
220
|
|
|
182
|
-
// src/client-identifier-
|
|
221
|
+
// src/client-identifier-prefix/z-client-id-prefix.ts
|
|
183
222
|
import { getGlobalConfig } from "@openid4vc/utils";
|
|
184
223
|
import { z as z7 } from "zod";
|
|
185
|
-
var
|
|
224
|
+
var zClientIdPrefix = z7.enum([
|
|
186
225
|
"pre-registered",
|
|
187
226
|
"redirect_uri",
|
|
188
|
-
"https",
|
|
189
227
|
"verifier_attestation",
|
|
228
|
+
"https",
|
|
229
|
+
// pre draft 26
|
|
230
|
+
"openid_federation",
|
|
231
|
+
// from draft 26
|
|
190
232
|
"did",
|
|
191
|
-
|
|
233
|
+
// pre draft 26
|
|
234
|
+
"decentralized_identifier",
|
|
235
|
+
// from draft 26
|
|
192
236
|
"x509_san_uri",
|
|
237
|
+
// pre-draft 25
|
|
238
|
+
"x509_hash",
|
|
239
|
+
// from draft 25
|
|
240
|
+
"x509_san_dns",
|
|
241
|
+
"origin",
|
|
242
|
+
// from draft 25
|
|
193
243
|
"web-origin"
|
|
244
|
+
// pre-draft 25
|
|
194
245
|
]);
|
|
195
|
-
var
|
|
246
|
+
var zUniformClientIdPrefix = zClientIdPrefix.exclude(["did", "https", "web-origin"]);
|
|
247
|
+
var zClientIdToClientIdPrefixAndIdentifier = z7.union(
|
|
196
248
|
[
|
|
197
249
|
z7.string({ message: "client_id MUST be a string" }).includes(":").transform((clientId) => {
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
250
|
+
const colonIndex = clientId.indexOf(":");
|
|
251
|
+
const clientIdPrefix = clientId.slice(0, colonIndex);
|
|
252
|
+
const clientIdIdentifier = clientId.slice(colonIndex + 1);
|
|
253
|
+
if (clientIdPrefix === "http" && getGlobalConfig().allowInsecureUrls) {
|
|
254
|
+
return ["https", clientId];
|
|
255
|
+
}
|
|
256
|
+
if (clientIdPrefix === "did" || clientIdPrefix === "http" || clientIdPrefix === "https") {
|
|
257
|
+
return [clientIdPrefix, clientId];
|
|
258
|
+
}
|
|
259
|
+
return [clientIdPrefix, clientIdIdentifier];
|
|
260
|
+
}).pipe(z7.tuple([zClientIdPrefix.exclude(["pre-registered"]), z7.string()])),
|
|
261
|
+
z7.string().refine((clientId) => clientId.includes(":") === false).transform((clientId) => ["pre-registered", clientId])
|
|
202
262
|
],
|
|
203
263
|
{
|
|
204
|
-
message: `client_id must either start with a known prefix followed by ':' or contain no ':'. Known prefixes are ${
|
|
264
|
+
message: `client_id must either start with a known prefix followed by ':' or contain no ':'. Known prefixes are ${zClientIdPrefix.exclude(["pre-registered"]).options.join(", ")}`
|
|
205
265
|
}
|
|
206
266
|
);
|
|
267
|
+
var zClientIdPrefixToUniform = zClientIdPrefix.transform(
|
|
268
|
+
(prefix) => prefix === "did" ? "decentralized_identifier" : prefix === "https" ? "openid_federation" : prefix === "web-origin" ? "origin" : prefix
|
|
269
|
+
);
|
|
207
270
|
var zLegacyClientIdScheme = z7.enum([
|
|
208
271
|
"pre-registered",
|
|
209
272
|
"redirect_uri",
|
|
@@ -213,10 +276,15 @@ var zLegacyClientIdScheme = z7.enum([
|
|
|
213
276
|
"x509_san_dns",
|
|
214
277
|
"x509_san_uri"
|
|
215
278
|
]);
|
|
216
|
-
var
|
|
279
|
+
var zLegacyClientIdSchemeToClientIdPrefix = zLegacyClientIdScheme.optional().default("pre-registered").transform(
|
|
280
|
+
(clientIdScheme) => clientIdScheme === "entity_id" ? "openid_federation" : clientIdScheme === "did" ? "decentralized_identifier" : clientIdScheme
|
|
281
|
+
);
|
|
217
282
|
|
|
218
|
-
// src/client-identifier-
|
|
283
|
+
// src/client-identifier-prefix/parse-client-identifier-prefix.ts
|
|
219
284
|
function getOpenid4vpClientId(options) {
|
|
285
|
+
const original = {
|
|
286
|
+
clientId: options.clientId
|
|
287
|
+
};
|
|
220
288
|
if (isOpenid4vpResponseModeDcApi(options.responseMode)) {
|
|
221
289
|
if (!options.clientId) {
|
|
222
290
|
if (!options.origin) {
|
|
@@ -226,20 +294,37 @@ function getOpenid4vpClientId(options) {
|
|
|
226
294
|
});
|
|
227
295
|
}
|
|
228
296
|
return {
|
|
229
|
-
|
|
230
|
-
|
|
297
|
+
clientIdPrefix: "origin",
|
|
298
|
+
effectiveClientIdPrefix: "origin",
|
|
299
|
+
clientIdIdentifier: options.origin,
|
|
300
|
+
// FIXME: draft 24 uses web-origin, draft 25+ uses origin
|
|
301
|
+
// But it's not really possible to know which one to use as the
|
|
302
|
+
// 'effective' client id. Defaulting to origin: since that's newer
|
|
303
|
+
effectiveClientId: `origin:${options.origin}`,
|
|
304
|
+
original
|
|
231
305
|
};
|
|
232
306
|
}
|
|
233
|
-
const
|
|
234
|
-
if (!
|
|
307
|
+
const parsedClientIdPrefixAndIdentifier2 = zClientIdToClientIdPrefixAndIdentifier.safeParse(options.clientId);
|
|
308
|
+
if (!parsedClientIdPrefixAndIdentifier2.success) {
|
|
309
|
+
throw new Oauth2ServerErrorResponseError({
|
|
310
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
311
|
+
error_description: `Failed to parse client identifier. Unsupported client_id '${options.clientId}'.`
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
const [clientIdScheme2, clientIdIdentifier2] = parsedClientIdPrefixAndIdentifier2.data;
|
|
315
|
+
const uniformClientIdScheme2 = zClientIdPrefixToUniform.safeParse(clientIdScheme2);
|
|
316
|
+
if (!uniformClientIdScheme2.success) {
|
|
235
317
|
throw new Oauth2ServerErrorResponseError({
|
|
236
318
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
237
319
|
error_description: `Failed to parse client identifier. Unsupported client_id '${options.clientId}'.`
|
|
238
320
|
});
|
|
239
321
|
}
|
|
240
322
|
return {
|
|
241
|
-
|
|
242
|
-
|
|
323
|
+
effectiveClientId: options.clientId,
|
|
324
|
+
effectiveClientIdPrefix: clientIdScheme2,
|
|
325
|
+
original,
|
|
326
|
+
clientIdPrefix: uniformClientIdScheme2.data,
|
|
327
|
+
clientIdIdentifier: clientIdIdentifier2
|
|
243
328
|
};
|
|
244
329
|
}
|
|
245
330
|
if (!options.clientId) {
|
|
@@ -249,62 +334,75 @@ function getOpenid4vpClientId(options) {
|
|
|
249
334
|
});
|
|
250
335
|
}
|
|
251
336
|
if (options.legacyClientIdScheme) {
|
|
252
|
-
const
|
|
253
|
-
if (!
|
|
337
|
+
const parsedClientIdPrefix = zLegacyClientIdSchemeToClientIdPrefix.safeParse(options.legacyClientIdScheme);
|
|
338
|
+
if (!parsedClientIdPrefix.success) {
|
|
254
339
|
throw new Oauth2ServerErrorResponseError({
|
|
255
340
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
256
341
|
error_description: `Failed to parse client identifier. Unsupported client_id_scheme value '${options.legacyClientIdScheme}'.`
|
|
257
342
|
});
|
|
258
343
|
}
|
|
259
|
-
const
|
|
344
|
+
const clientIdPrefix = parsedClientIdPrefix.data;
|
|
260
345
|
return {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
346
|
+
effectiveClientId: options.clientId,
|
|
347
|
+
clientIdIdentifier: options.clientId,
|
|
348
|
+
clientIdPrefix,
|
|
349
|
+
effectiveClientIdPrefix: options.legacyClientIdScheme ?? "pre-registered",
|
|
350
|
+
original: {
|
|
351
|
+
...original,
|
|
352
|
+
clientIdScheme: options.legacyClientIdScheme
|
|
353
|
+
}
|
|
264
354
|
};
|
|
265
355
|
}
|
|
266
|
-
const
|
|
267
|
-
if (!
|
|
356
|
+
const parsedClientIdPrefixAndIdentifier = zClientIdToClientIdPrefixAndIdentifier.safeParse(options.clientId);
|
|
357
|
+
if (!parsedClientIdPrefixAndIdentifier.success) {
|
|
358
|
+
throw new Oauth2ServerErrorResponseError({
|
|
359
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
360
|
+
error_description: `Failed to parse client identifier. Unsupported client_id '${options.clientId}'.`
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
const [clientIdScheme, clientIdIdentifier] = parsedClientIdPrefixAndIdentifier.data;
|
|
364
|
+
const uniformClientIdScheme = zClientIdPrefixToUniform.safeParse(clientIdScheme);
|
|
365
|
+
if (!uniformClientIdScheme.success) {
|
|
268
366
|
throw new Oauth2ServerErrorResponseError({
|
|
269
367
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
270
368
|
error_description: `Failed to parse client identifier. Unsupported client_id '${options.clientId}'.`
|
|
271
369
|
});
|
|
272
370
|
}
|
|
273
371
|
return {
|
|
274
|
-
|
|
275
|
-
|
|
372
|
+
effectiveClientId: options.clientId,
|
|
373
|
+
clientIdPrefix: uniformClientIdScheme.data,
|
|
374
|
+
effectiveClientIdPrefix: clientIdScheme,
|
|
375
|
+
clientIdIdentifier,
|
|
376
|
+
original
|
|
276
377
|
};
|
|
277
378
|
}
|
|
278
|
-
function validateOpenid4vpClientId(options, parserConfig) {
|
|
379
|
+
async function validateOpenid4vpClientId(options, parserConfig) {
|
|
279
380
|
const { authorizationRequestPayload, jar, origin } = options;
|
|
280
381
|
const parserConfigWithDefaults = {
|
|
281
|
-
supportedSchemes: parserConfig?.supportedSchemes || Object.values(
|
|
382
|
+
supportedSchemes: parserConfig?.supportedSchemes || Object.values(zClientIdPrefix.options)
|
|
282
383
|
};
|
|
283
|
-
const {
|
|
384
|
+
const { clientIdIdentifier, clientIdPrefix, effectiveClientId, original } = getOpenid4vpClientId({
|
|
284
385
|
clientId: authorizationRequestPayload.client_id,
|
|
285
386
|
legacyClientIdScheme: authorizationRequestPayload.client_id_scheme,
|
|
286
387
|
responseMode: authorizationRequestPayload.response_mode,
|
|
287
388
|
origin
|
|
288
389
|
});
|
|
289
|
-
if (
|
|
390
|
+
if (clientIdPrefix === "pre-registered") {
|
|
290
391
|
return {
|
|
291
|
-
|
|
292
|
-
identifier:
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
clientMetadata: authorizationRequestPayload.client_metadata
|
|
392
|
+
prefix: "pre-registered",
|
|
393
|
+
identifier: clientIdIdentifier,
|
|
394
|
+
effective: effectiveClientId,
|
|
395
|
+
original
|
|
296
396
|
};
|
|
297
397
|
}
|
|
298
|
-
|
|
299
|
-
const identifierPart = clientId.substring(colonIndex + 1);
|
|
300
|
-
if (!parserConfigWithDefaults.supportedSchemes.includes(clientIdScheme)) {
|
|
398
|
+
if (!parserConfigWithDefaults.supportedSchemes.includes(clientIdPrefix)) {
|
|
301
399
|
throw new Oauth2ServerErrorResponseError({
|
|
302
400
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
303
|
-
error_description: `Unsupported client identifier scheme. ${
|
|
401
|
+
error_description: `Unsupported client identifier scheme. ${clientIdPrefix} is not supported.`
|
|
304
402
|
});
|
|
305
403
|
}
|
|
306
|
-
if (
|
|
307
|
-
if (!zHttpsUrl3.safeParse(
|
|
404
|
+
if (clientIdPrefix === "openid_federation") {
|
|
405
|
+
if (!zHttpsUrl3.safeParse(clientIdIdentifier).success) {
|
|
308
406
|
throw new Oauth2ServerErrorResponseError(
|
|
309
407
|
{
|
|
310
408
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
@@ -328,14 +426,14 @@ function validateOpenid4vpClientId(options, parserConfig) {
|
|
|
328
426
|
});
|
|
329
427
|
}
|
|
330
428
|
return {
|
|
331
|
-
|
|
332
|
-
identifier:
|
|
333
|
-
|
|
334
|
-
|
|
429
|
+
prefix: "openid_federation",
|
|
430
|
+
identifier: clientIdIdentifier,
|
|
431
|
+
effective: effectiveClientId,
|
|
432
|
+
original,
|
|
335
433
|
trustChain: authorizationRequestPayload.trust_chain
|
|
336
434
|
};
|
|
337
435
|
}
|
|
338
|
-
if (
|
|
436
|
+
if (clientIdPrefix === "redirect_uri") {
|
|
339
437
|
if (jar) {
|
|
340
438
|
throw new Oauth2ServerErrorResponseError({
|
|
341
439
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
@@ -349,14 +447,15 @@ function validateOpenid4vpClientId(options, parserConfig) {
|
|
|
349
447
|
});
|
|
350
448
|
}
|
|
351
449
|
return {
|
|
352
|
-
|
|
353
|
-
identifier:
|
|
354
|
-
|
|
355
|
-
|
|
450
|
+
prefix: clientIdPrefix,
|
|
451
|
+
identifier: clientIdIdentifier,
|
|
452
|
+
effective: effectiveClientId,
|
|
453
|
+
original,
|
|
454
|
+
clientMetadata: authorizationRequestPayload.client_metadata,
|
|
356
455
|
redirectUri: authorizationRequestPayload.redirect_uri ?? authorizationRequestPayload.response_uri
|
|
357
456
|
};
|
|
358
457
|
}
|
|
359
|
-
if (
|
|
458
|
+
if (clientIdPrefix === "decentralized_identifier") {
|
|
360
459
|
if (!jar) {
|
|
361
460
|
throw new Oauth2ServerErrorResponseError({
|
|
362
461
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
@@ -369,113 +468,115 @@ function validateOpenid4vpClientId(options, parserConfig) {
|
|
|
369
468
|
error_description: "Something went wrong. The JWT signer method is not did but the client identifier scheme is did."
|
|
370
469
|
});
|
|
371
470
|
}
|
|
372
|
-
if (!
|
|
471
|
+
if (!clientIdIdentifier.startsWith("did:")) {
|
|
373
472
|
throw new Oauth2ServerErrorResponseError({
|
|
374
473
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
375
|
-
error_description: "Invalid client identifier. Client identifier must start with 'did:'"
|
|
474
|
+
error_description: "Invalid client identifier. Client id identifier must start with 'did:'"
|
|
376
475
|
});
|
|
377
476
|
}
|
|
378
477
|
const [did] = jar.signer.didUrl.split("#");
|
|
379
|
-
if (
|
|
478
|
+
if (clientIdIdentifier !== did) {
|
|
380
479
|
throw new Oauth2ServerErrorResponseError({
|
|
381
480
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
382
|
-
error_description:
|
|
481
|
+
error_description: `With client identifier scheme '${clientIdPrefix}' the JAR request must be signed by the same DID as the client identifier.`
|
|
383
482
|
});
|
|
384
483
|
}
|
|
385
484
|
return {
|
|
386
|
-
|
|
387
|
-
identifier:
|
|
388
|
-
|
|
389
|
-
|
|
485
|
+
prefix: "decentralized_identifier",
|
|
486
|
+
identifier: clientIdIdentifier,
|
|
487
|
+
effective: effectiveClientId,
|
|
488
|
+
original,
|
|
489
|
+
clientMetadata: authorizationRequestPayload.client_metadata,
|
|
390
490
|
didUrl: jar.signer.didUrl
|
|
391
491
|
};
|
|
392
492
|
}
|
|
393
|
-
if (
|
|
493
|
+
if (clientIdPrefix === "x509_san_dns" || clientIdPrefix === "x509_san_uri" || clientIdPrefix === "x509_hash") {
|
|
394
494
|
if (!jar) {
|
|
395
495
|
throw new Oauth2ServerErrorResponseError({
|
|
396
496
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
397
|
-
error_description:
|
|
497
|
+
error_description: `Using client identifier scheme '${clientIdPrefix}' requires a signed JAR request.`
|
|
398
498
|
});
|
|
399
499
|
}
|
|
400
500
|
if (jar.signer.method !== "x5c") {
|
|
401
501
|
throw new Oauth2ServerErrorResponseError({
|
|
402
502
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
403
|
-
error_description:
|
|
503
|
+
error_description: `Something went wrong. The JWT signer method is not x5c but the client identifier scheme is '${clientIdPrefix}'`
|
|
404
504
|
});
|
|
405
505
|
}
|
|
406
|
-
if (
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
{
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
506
|
+
if (!options.callbacks.getX509CertificateMetadata) {
|
|
507
|
+
throw new Oauth2ServerErrorResponseError(
|
|
508
|
+
{
|
|
509
|
+
error: Oauth2ErrorCodes.ServerError
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
internalMessage: `Missing required 'getX509CertificateMetadata' callback for verification of '${clientIdPrefix}' client id scheme`
|
|
513
|
+
}
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
if (clientIdPrefix === "x509_san_dns") {
|
|
417
517
|
const { sanDnsNames } = options.callbacks.getX509CertificateMetadata(jar.signer.x5c[0]);
|
|
418
|
-
if (!sanDnsNames.includes(
|
|
518
|
+
if (!sanDnsNames.includes(clientIdIdentifier)) {
|
|
419
519
|
throw new Oauth2ServerErrorResponseError({
|
|
420
520
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
421
|
-
error_description: `Invalid client identifier. One of the leaf certificates san dns names [${sanDnsNames.join(", ")}] must match the client identifier '${
|
|
521
|
+
error_description: `Invalid client identifier. One of the leaf certificates san dns names [${sanDnsNames.join(", ")}] must match the client identifier '${clientIdIdentifier}'. `
|
|
422
522
|
});
|
|
423
523
|
}
|
|
424
524
|
if (!isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
|
|
425
525
|
const uri = authorizationRequestPayload.redirect_uri ?? authorizationRequestPayload.response_uri;
|
|
426
|
-
if (!uri || new URL2(uri).hostname !==
|
|
526
|
+
if (!uri || new URL2(uri).hostname !== clientIdIdentifier) {
|
|
427
527
|
throw new Oauth2ServerErrorResponseError({
|
|
428
528
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
429
529
|
error_description: "Invalid client identifier. The fully qualified domain name of the redirect_uri value MUST match the Client Identifier without the prefix x509_san_dns."
|
|
430
530
|
});
|
|
431
531
|
}
|
|
432
532
|
}
|
|
433
|
-
} else if (
|
|
434
|
-
if (!options.callbacks.getX509CertificateMetadata) {
|
|
435
|
-
throw new Oauth2ServerErrorResponseError(
|
|
436
|
-
{
|
|
437
|
-
error: Oauth2ErrorCodes.ServerError
|
|
438
|
-
},
|
|
439
|
-
{
|
|
440
|
-
internalMessage: "Missing required 'getX509CertificateMetadata' callback for verification of 'x509_san_uri' client id scheme"
|
|
441
|
-
}
|
|
442
|
-
);
|
|
443
|
-
}
|
|
533
|
+
} else if (clientIdPrefix === "x509_san_uri") {
|
|
444
534
|
const { sanUriNames } = options.callbacks.getX509CertificateMetadata(jar.signer.x5c[0]);
|
|
445
|
-
if (!sanUriNames.includes(
|
|
535
|
+
if (!sanUriNames.includes(clientIdIdentifier)) {
|
|
446
536
|
throw new Oauth2ServerErrorResponseError({
|
|
447
537
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
448
|
-
error_description: `Invalid client identifier. One of the leaf certificates san uri names [${sanUriNames.join(", ")}] must match the client identifier '${
|
|
538
|
+
error_description: `Invalid client identifier. One of the leaf certificates san uri names [${sanUriNames.join(", ")}] must match the client identifier '${clientIdIdentifier}'.`
|
|
449
539
|
});
|
|
450
540
|
}
|
|
451
541
|
if (!isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
|
|
452
542
|
const uri = authorizationRequestPayload.redirect_uri || authorizationRequestPayload.response_uri;
|
|
453
|
-
if (!uri || uri !==
|
|
543
|
+
if (!uri || uri !== clientIdIdentifier) {
|
|
454
544
|
throw new Oauth2ServerErrorResponseError({
|
|
455
545
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
456
546
|
error_description: "The redirect_uri value MUST match the Client Identifier without the prefix x509_san_uri"
|
|
457
547
|
});
|
|
458
548
|
}
|
|
459
549
|
}
|
|
550
|
+
} else if (clientIdPrefix === "x509_hash") {
|
|
551
|
+
const x509Hash = encodeToBase64Url(
|
|
552
|
+
await options.callbacks.hash(decodeBase64(jar.signer.x5c[0]), HashAlgorithm.Sha256)
|
|
553
|
+
);
|
|
554
|
+
if (x509Hash !== clientIdIdentifier) {
|
|
555
|
+
throw new Oauth2ServerErrorResponseError({
|
|
556
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
557
|
+
error_description: `Invalid client identifier. Expected the base64url encoded sha-256 hash of the leaf x5c certificate ('${x509Hash}') to match the client identifier '${clientIdIdentifier}'.`
|
|
558
|
+
});
|
|
559
|
+
}
|
|
460
560
|
}
|
|
461
561
|
return {
|
|
462
|
-
|
|
463
|
-
identifier:
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
x5c: jar.signer.x5c
|
|
562
|
+
prefix: clientIdPrefix,
|
|
563
|
+
identifier: clientIdIdentifier,
|
|
564
|
+
effective: effectiveClientId,
|
|
565
|
+
original,
|
|
566
|
+
x5c: jar.signer.x5c,
|
|
567
|
+
clientMetadata: authorizationRequestPayload.client_metadata
|
|
467
568
|
};
|
|
468
569
|
}
|
|
469
|
-
if (
|
|
570
|
+
if (clientIdPrefix === "origin") {
|
|
470
571
|
return {
|
|
471
|
-
|
|
472
|
-
identifier:
|
|
473
|
-
|
|
474
|
-
|
|
572
|
+
prefix: clientIdPrefix,
|
|
573
|
+
identifier: clientIdIdentifier,
|
|
574
|
+
effective: effectiveClientId,
|
|
575
|
+
original,
|
|
475
576
|
clientMetadata: authorizationRequestPayload.client_metadata
|
|
476
577
|
};
|
|
477
578
|
}
|
|
478
|
-
if (
|
|
579
|
+
if (clientIdPrefix === "verifier_attestation") {
|
|
479
580
|
if (!jar) {
|
|
480
581
|
throw new Oauth2ServerErrorResponseError({
|
|
481
582
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
@@ -484,10 +585,11 @@ function validateOpenid4vpClientId(options, parserConfig) {
|
|
|
484
585
|
}
|
|
485
586
|
}
|
|
486
587
|
return {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
588
|
+
prefix: clientIdPrefix,
|
|
589
|
+
clientMetadata: authorizationRequestPayload.client_metadata,
|
|
590
|
+
identifier: clientIdIdentifier,
|
|
591
|
+
effective: effectiveClientId,
|
|
592
|
+
original
|
|
491
593
|
};
|
|
492
594
|
}
|
|
493
595
|
|
|
@@ -495,6 +597,7 @@ function validateOpenid4vpClientId(options, parserConfig) {
|
|
|
495
597
|
import {
|
|
496
598
|
Oauth2Error as Oauth2Error3,
|
|
497
599
|
decodeJwt,
|
|
600
|
+
decodeJwtHeader,
|
|
498
601
|
jwtSignerFromJwt,
|
|
499
602
|
zCompactJwe,
|
|
500
603
|
zCompactJwt,
|
|
@@ -503,15 +606,18 @@ import {
|
|
|
503
606
|
import z9 from "zod";
|
|
504
607
|
|
|
505
608
|
// src/jarm/jarm-extract-jwks.ts
|
|
506
|
-
function
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
609
|
+
function extractJwkFromJwks(jwks, {
|
|
610
|
+
kid,
|
|
611
|
+
supportedAlgValues
|
|
612
|
+
}) {
|
|
613
|
+
if (kid) {
|
|
614
|
+
return jwks.keys.find((jwk) => jwk.kid === kid);
|
|
615
|
+
}
|
|
616
|
+
let algFiltered = jwks.keys.filter((key) => key.alg && supportedAlgValues?.includes(key.alg));
|
|
617
|
+
if (algFiltered.length === 0) algFiltered = jwks.keys;
|
|
618
|
+
let encFiltered = algFiltered.filter((key) => key.use === "enc");
|
|
619
|
+
if (!encFiltered) encFiltered = algFiltered.filter((key) => key.use !== "sig");
|
|
620
|
+
return encFiltered.length > 0 ? encFiltered[0] : jwks.keys[0];
|
|
515
621
|
}
|
|
516
622
|
|
|
517
623
|
// src/jarm/jarm-authorization-response/jarm-validate-authorization-response.ts
|
|
@@ -562,10 +668,18 @@ var JarmMode = /* @__PURE__ */ ((JarmMode2) => {
|
|
|
562
668
|
})(JarmMode || {});
|
|
563
669
|
var decryptJarmAuthorizationResponseJwt = async (options) => {
|
|
564
670
|
const { jarmAuthorizationResponseJwt, callbacks, authorizationRequestPayload } = options;
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
})
|
|
671
|
+
let encryptionJwk = void 0;
|
|
672
|
+
const { header } = decodeJwtHeader({
|
|
673
|
+
jwt: jarmAuthorizationResponseJwt
|
|
674
|
+
});
|
|
675
|
+
if (authorizationRequestPayload.client_metadata?.jwks) {
|
|
676
|
+
encryptionJwk = extractJwkFromJwks(authorizationRequestPayload.client_metadata.jwks, {
|
|
677
|
+
// Kid always take precedence
|
|
678
|
+
kid: header.kid,
|
|
679
|
+
// This value was removed in draft 26, but if it's still provided, we can use it to determine the key to use
|
|
680
|
+
supportedAlgValues: authorizationRequestPayload.client_metadata.authorization_encrypted_response_alg ? [authorizationRequestPayload.client_metadata.authorization_encrypted_response_alg] : void 0
|
|
681
|
+
});
|
|
682
|
+
}
|
|
569
683
|
const result = await callbacks.decryptJwe(jarmAuthorizationResponseJwt, { jwk: encryptionJwk });
|
|
570
684
|
if (!result.decrypted) {
|
|
571
685
|
throw new Oauth2Error3("Failed to decrypt jarm auth response.");
|
|
@@ -703,10 +817,10 @@ var validateOpenid4vpAuthorizationRequestPayload = (options) => {
|
|
|
703
817
|
error_description: 'The "wallet_nonce" parameter MUST match the "expectedNonce" parameter when the "expectedNonce" parameter is provided.'
|
|
704
818
|
});
|
|
705
819
|
}
|
|
706
|
-
if (params.client_id.startsWith("web-origin:")) {
|
|
820
|
+
if (params.client_id.startsWith("web-origin:") || params.client_id.startsWith("origin:")) {
|
|
707
821
|
throw new Oauth2ServerErrorResponseError2({
|
|
708
822
|
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
709
|
-
error_description: `The 'client_id' parameter MUST NOT use client identifier scheme '
|
|
823
|
+
error_description: `The 'client_id' parameter MUST NOT use client identifier scheme '${params.client_id.split(":")[0]}' when not using the dc_api response mode. Current: ${params.client_id}`
|
|
710
824
|
});
|
|
711
825
|
}
|
|
712
826
|
};
|
|
@@ -942,6 +1056,48 @@ import z13 from "zod";
|
|
|
942
1056
|
import { Oauth2ErrorCodes as Oauth2ErrorCodes5, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError6 } from "@openid4vc/oauth2";
|
|
943
1057
|
function parseAuthorizationRequestVersion(request) {
|
|
944
1058
|
const requirements = [];
|
|
1059
|
+
if (request.verifier_info) {
|
|
1060
|
+
requirements.push([">=", 29]);
|
|
1061
|
+
}
|
|
1062
|
+
if (request.verifier_attestations) {
|
|
1063
|
+
requirements.push(["<", 29]);
|
|
1064
|
+
}
|
|
1065
|
+
if (request.client_metadata?.vp_formats_supported?.mso_mdoc?.deviceauth_alg_values || request.client_metadata?.vp_formats_supported?.mso_mdoc?.deviceauth_alg_values) {
|
|
1066
|
+
requirements.push([">=", 28]);
|
|
1067
|
+
}
|
|
1068
|
+
if (request.client_metadata?.vp_formats_supported?.mso_mdoc?.issuer_signed_alg_values || request.client_metadata?.vp_formats_supported?.mso_mdoc?.device_signed_alg_values) {
|
|
1069
|
+
requirements.push(["<", 28]);
|
|
1070
|
+
}
|
|
1071
|
+
if (request.client_metadata?.vp_formats) {
|
|
1072
|
+
requirements.push([">=", 27]);
|
|
1073
|
+
}
|
|
1074
|
+
if (request.client_metadata?.vp_formats_supported) {
|
|
1075
|
+
requirements.push(["<", 27]);
|
|
1076
|
+
}
|
|
1077
|
+
if (request.client_id?.startsWith("openid_federation:") || request.client_id?.startsWith("decentralized_identifier:")) {
|
|
1078
|
+
requirements.push([">=", 26]);
|
|
1079
|
+
}
|
|
1080
|
+
if (request.client_id?.startsWith("did:")) {
|
|
1081
|
+
requirements.push(["<", 26]);
|
|
1082
|
+
}
|
|
1083
|
+
if (request.presentation_definition || request.presentation_definition_uri) {
|
|
1084
|
+
requirements.push([">=", 26]);
|
|
1085
|
+
}
|
|
1086
|
+
if (request.verifier_attestations) {
|
|
1087
|
+
requirements.push([">=", 26]);
|
|
1088
|
+
}
|
|
1089
|
+
if (request.client_id?.startsWith("x509_san_uri:")) {
|
|
1090
|
+
requirements.push(["<", 25]);
|
|
1091
|
+
}
|
|
1092
|
+
if (request.client_id?.startsWith("x509_hash:")) {
|
|
1093
|
+
requirements.push([">=", 25]);
|
|
1094
|
+
}
|
|
1095
|
+
if (request.client_id?.startsWith("web-origin:")) {
|
|
1096
|
+
requirements.push(["<", 25]);
|
|
1097
|
+
}
|
|
1098
|
+
if (request.client_id?.startsWith("origin:")) {
|
|
1099
|
+
requirements.push([">=", 25]);
|
|
1100
|
+
}
|
|
945
1101
|
if (isOpenid4vpAuthorizationRequestDcApi(request) && (request.response_mode === "w3c_dc_api" || request.response_mode === "w3c_dc_api.jwt")) {
|
|
946
1102
|
requirements.push(["<", 23]);
|
|
947
1103
|
requirements.push([">=", 21]);
|
|
@@ -961,7 +1117,7 @@ function parseAuthorizationRequestVersion(request) {
|
|
|
961
1117
|
if (request.client_id) {
|
|
962
1118
|
const colonIndex = request.client_id.indexOf(":");
|
|
963
1119
|
const schemePart = request.client_id.substring(0, colonIndex);
|
|
964
|
-
const parsedScheme =
|
|
1120
|
+
const parsedScheme = zClientIdPrefix.safeParse(schemePart);
|
|
965
1121
|
if (parsedScheme.success && parsedScheme.data !== "did" && parsedScheme.data !== "https") {
|
|
966
1122
|
requirements.push([">=", 22]);
|
|
967
1123
|
}
|
|
@@ -969,6 +1125,9 @@ function parseAuthorizationRequestVersion(request) {
|
|
|
969
1125
|
if (!request.client_id) {
|
|
970
1126
|
requirements.push([">=", 21]);
|
|
971
1127
|
}
|
|
1128
|
+
if (request.dcql_query) {
|
|
1129
|
+
requirements.push([">=", 21]);
|
|
1130
|
+
}
|
|
972
1131
|
if (request.client_metadata_uri) {
|
|
973
1132
|
requirements.push(["<", 21]);
|
|
974
1133
|
}
|
|
@@ -986,7 +1145,7 @@ function parseAuthorizationRequestVersion(request) {
|
|
|
986
1145
|
}
|
|
987
1146
|
const lessThanVersions = requirements.filter(([operator]) => operator === "<").map(([_, version]) => version);
|
|
988
1147
|
const greaterThanVersions = requirements.filter(([operator]) => operator === ">=").map(([_, version]) => version);
|
|
989
|
-
const highestPossibleVersion = lessThanVersions.length > 0 ? Math.max(Math.min(...lessThanVersions) - 1, 18) :
|
|
1148
|
+
const highestPossibleVersion = lessThanVersions.length > 0 ? Math.max(Math.min(...lessThanVersions) - 1, 18) : 29;
|
|
990
1149
|
const lowestRequiredVersion = greaterThanVersions.length > 0 ? Math.max(...greaterThanVersions) : 18;
|
|
991
1150
|
if (lowestRequiredVersion > highestPossibleVersion) {
|
|
992
1151
|
throw new Oauth2ServerErrorResponseError6({
|
|
@@ -1044,7 +1203,7 @@ async function verifyJarRequest(options) {
|
|
|
1044
1203
|
const { callbacks, wallet = {} } = options;
|
|
1045
1204
|
const jarRequestParams = validateJarRequestParams(options);
|
|
1046
1205
|
const sendBy = jarRequestParams.request ? "value" : "reference";
|
|
1047
|
-
const clientIdentifierScheme = jarRequestParams.client_id ?
|
|
1206
|
+
const clientIdentifierScheme = jarRequestParams.client_id ? zClientIdPrefix.safeParse(jarRequestParams.client_id.split(":")[0]).data : "origin";
|
|
1048
1207
|
const method = jarRequestParams.request_uri_method ?? "get";
|
|
1049
1208
|
if (method !== "get" && method !== "post") {
|
|
1050
1209
|
throw new Oauth2ServerErrorResponseError8({
|
|
@@ -1120,15 +1279,15 @@ async function verifyJarRequestObject(options) {
|
|
|
1120
1279
|
const { decryptedRequestObject, callbacks } = options;
|
|
1121
1280
|
const jwt = decodeJwt3({ jwt: decryptedRequestObject, payloadSchema: zJarRequestObjectPayload });
|
|
1122
1281
|
let jwtSigner;
|
|
1123
|
-
const {
|
|
1282
|
+
const { clientIdPrefix } = getOpenid4vpClientId({
|
|
1124
1283
|
responseMode: jwt.payload.response_mode,
|
|
1125
1284
|
clientId: jwt.payload.client_id,
|
|
1126
1285
|
legacyClientIdScheme: jwt.payload.client_id_scheme
|
|
1127
1286
|
});
|
|
1128
1287
|
const clientIdToSignerMethod = {
|
|
1129
|
-
|
|
1288
|
+
decentralized_identifier: ["did"],
|
|
1130
1289
|
"pre-registered": ["custom", "did", "jwk"],
|
|
1131
|
-
|
|
1290
|
+
origin: [],
|
|
1132
1291
|
// no signing allowed
|
|
1133
1292
|
redirect_uri: [],
|
|
1134
1293
|
// no signing allowed
|
|
@@ -1136,10 +1295,11 @@ async function verifyJarRequestObject(options) {
|
|
|
1136
1295
|
verifier_attestation: ["did", "federation", "jwk", "x5c", "custom"],
|
|
1137
1296
|
x509_san_dns: ["x5c"],
|
|
1138
1297
|
x509_san_uri: ["x5c"],
|
|
1298
|
+
x509_hash: ["x5c"],
|
|
1139
1299
|
// Handled separately
|
|
1140
|
-
|
|
1300
|
+
openid_federation: []
|
|
1141
1301
|
};
|
|
1142
|
-
if (
|
|
1302
|
+
if (clientIdPrefix === "openid_federation") {
|
|
1143
1303
|
if (!jwt.header.kid) {
|
|
1144
1304
|
throw new Oauth2Error5(
|
|
1145
1305
|
`When OpenID Federation is used for signed authorization request, the 'kid' parameter is required.`
|
|
@@ -1152,7 +1312,7 @@ async function verifyJarRequestObject(options) {
|
|
|
1152
1312
|
kid: jwt.header.kid
|
|
1153
1313
|
};
|
|
1154
1314
|
} else {
|
|
1155
|
-
jwtSigner = jwtSignerFromJwt2({ ...jwt, allowedSignerMethods: clientIdToSignerMethod[
|
|
1315
|
+
jwtSigner = jwtSignerFromJwt2({ ...jwt, allowedSignerMethods: clientIdToSignerMethod[clientIdPrefix] });
|
|
1156
1316
|
}
|
|
1157
1317
|
const { signer } = await verifyJwt({
|
|
1158
1318
|
verifyJwtCallback: callbacks.verifyJwt,
|
|
@@ -1177,7 +1337,7 @@ async function verifyJarRequestObject(options) {
|
|
|
1177
1337
|
|
|
1178
1338
|
// src/transaction-data/parse-transaction-data.ts
|
|
1179
1339
|
import { Oauth2ErrorCodes as Oauth2ErrorCodes8, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError9 } from "@openid4vc/oauth2";
|
|
1180
|
-
import { decodeBase64, encodeToUtf8String, parseIfJson } from "@openid4vc/utils";
|
|
1340
|
+
import { decodeBase64 as decodeBase642, encodeToUtf8String, parseIfJson } from "@openid4vc/utils";
|
|
1181
1341
|
|
|
1182
1342
|
// src/transaction-data/z-transaction-data.ts
|
|
1183
1343
|
import { z as z14 } from "zod";
|
|
@@ -1191,7 +1351,7 @@ var zTransactionData = z14.array(zTransactionEntry);
|
|
|
1191
1351
|
// src/transaction-data/parse-transaction-data.ts
|
|
1192
1352
|
function parseTransactionData(options) {
|
|
1193
1353
|
const { transactionData } = options;
|
|
1194
|
-
const decoded = transactionData.map((tdEntry) => parseIfJson(encodeToUtf8String(
|
|
1354
|
+
const decoded = transactionData.map((tdEntry) => parseIfJson(encodeToUtf8String(decodeBase642(tdEntry))));
|
|
1195
1355
|
const parsedResult = zTransactionData.safeParse(decoded);
|
|
1196
1356
|
if (!parsedResult.success) {
|
|
1197
1357
|
throw new Oauth2ServerErrorResponseError9({
|
|
@@ -1243,7 +1403,7 @@ async function resolveOpenid4vpAuthorizationRequest(options) {
|
|
|
1243
1403
|
if (!isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload) && !clientMetadata && authorizationRequestPayload.client_metadata_uri) {
|
|
1244
1404
|
clientMetadata = await fetchClientMetadata({ clientMetadataUri: authorizationRequestPayload.client_metadata_uri });
|
|
1245
1405
|
}
|
|
1246
|
-
const clientMeta = validateOpenid4vpClientId({
|
|
1406
|
+
const clientMeta = await validateOpenid4vpClientId({
|
|
1247
1407
|
authorizationRequestPayload: {
|
|
1248
1408
|
...authorizationRequestPayload,
|
|
1249
1409
|
client_metadata: clientMetadata
|
|
@@ -1276,7 +1436,8 @@ async function resolveOpenid4vpAuthorizationRequest(options) {
|
|
|
1276
1436
|
jar,
|
|
1277
1437
|
client: clientMeta,
|
|
1278
1438
|
pex,
|
|
1279
|
-
dcql
|
|
1439
|
+
dcql,
|
|
1440
|
+
version: parseAuthorizationRequestVersion(authorizationRequestPayload)
|
|
1280
1441
|
};
|
|
1281
1442
|
}
|
|
1282
1443
|
function validateOpenId4vpAuthorizationRequestPayload(options) {
|
|
@@ -1304,7 +1465,7 @@ import {
|
|
|
1304
1465
|
Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError11,
|
|
1305
1466
|
fetchJwks
|
|
1306
1467
|
} from "@openid4vc/oauth2";
|
|
1307
|
-
import { dateToSeconds as dateToSeconds3, encodeToBase64Url } from "@openid4vc/utils";
|
|
1468
|
+
import { dateToSeconds as dateToSeconds3, encodeToBase64Url as encodeToBase64Url2 } from "@openid4vc/utils";
|
|
1308
1469
|
|
|
1309
1470
|
// ../utils/src/date.ts
|
|
1310
1471
|
function addSecondsToDate2(date, seconds) {
|
|
@@ -1401,7 +1562,7 @@ async function createOpenid4vpAuthorizationResponse(options) {
|
|
|
1401
1562
|
...options.authorizationResponsePayload,
|
|
1402
1563
|
state: authorizationRequestPayload.state
|
|
1403
1564
|
};
|
|
1404
|
-
const {
|
|
1565
|
+
const { clientIdPrefix } = getOpenid4vpClientId({
|
|
1405
1566
|
responseMode: authorizationRequestPayload.response_mode,
|
|
1406
1567
|
clientId: authorizationRequestPayload.client_id,
|
|
1407
1568
|
legacyClientIdScheme: authorizationRequestPayload.client_id_scheme,
|
|
@@ -1417,9 +1578,9 @@ async function createOpenid4vpAuthorizationResponse(options) {
|
|
|
1417
1578
|
authorizationResponsePayload
|
|
1418
1579
|
};
|
|
1419
1580
|
}
|
|
1420
|
-
if (
|
|
1581
|
+
if (clientIdPrefix === "openid_federation" && !options.clientMetadata) {
|
|
1421
1582
|
throw new Oauth2Error8(
|
|
1422
|
-
"When OpenID Federation is used as the client id scheme (https), passing externally fetched and verified 'clientMetadata' to the 'createOpenid4vpAuthorizationResponse' is required."
|
|
1583
|
+
"When OpenID Federation is used as the client id scheme (https/openid_federation), passing externally fetched and verified 'clientMetadata' to the 'createOpenid4vpAuthorizationResponse' is required."
|
|
1423
1584
|
);
|
|
1424
1585
|
}
|
|
1425
1586
|
const clientMetadata = options.clientMetadata ?? authorizationRequestPayload.client_metadata;
|
|
@@ -1437,20 +1598,40 @@ async function createOpenid4vpAuthorizationResponse(options) {
|
|
|
1437
1598
|
error_description: `Missing 'jwks' or 'jwks_uri' in client metadata. Cannot extract encryption JWK.`
|
|
1438
1599
|
});
|
|
1439
1600
|
}
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1601
|
+
if (clientMetadata.authorization_encrypted_response_alg || clientMetadata.authorization_encrypted_response_env || clientMetadata.authorization_signed_response_alg) {
|
|
1602
|
+
jarmAssertMetadataSupported({
|
|
1603
|
+
clientMetadata,
|
|
1604
|
+
serverMetadata: jarm.serverMetadata
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
const encJwk = extractJwkFromJwks(jwks, {
|
|
1608
|
+
supportedAlgValues: jarm.serverMetadata.authorization_encryption_alg_values_supported ?? (clientMetadata.authorization_encrypted_response_alg ? [clientMetadata.authorization_encrypted_response_alg] : void 0)
|
|
1447
1609
|
});
|
|
1448
|
-
if (!
|
|
1610
|
+
if (!encJwk) {
|
|
1449
1611
|
throw new Oauth2ServerErrorResponseError11({
|
|
1450
1612
|
error: Oauth2ErrorCodes10.InvalidRequest,
|
|
1451
1613
|
error_description: "Could not extract encryption JWK from client metadata. Failed to create JARM response."
|
|
1452
1614
|
});
|
|
1453
1615
|
}
|
|
1616
|
+
let enc;
|
|
1617
|
+
if (clientMetadata.encrypted_response_enc_values_supported) {
|
|
1618
|
+
enc = jarm.serverMetadata.authorization_encryption_enc_values_supported.find(
|
|
1619
|
+
(enc2) => clientMetadata.encrypted_response_enc_values_supported?.includes(enc2)
|
|
1620
|
+
) ?? clientMetadata.encrypted_response_enc_values_supported[0];
|
|
1621
|
+
} else {
|
|
1622
|
+
enc = clientMetadata.authorization_encrypted_response_enc ?? "A128GCM";
|
|
1623
|
+
}
|
|
1624
|
+
assertValueSupported({
|
|
1625
|
+
actual: enc,
|
|
1626
|
+
supported: jarm.serverMetadata.authorization_encryption_enc_values_supported,
|
|
1627
|
+
errorMessage: `Invalid 'enc' value ${enc}. Supported values are ${jarm.serverMetadata.authorization_encryption_enc_values_supported.join(", ")}`
|
|
1628
|
+
});
|
|
1629
|
+
const alg = encJwk.alg ?? clientMetadata.authorization_encrypted_response_alg ?? "ECDH-ES";
|
|
1630
|
+
assertValueSupported({
|
|
1631
|
+
actual: alg,
|
|
1632
|
+
supported: jarm.serverMetadata.authorization_encryption_alg_values_supported,
|
|
1633
|
+
errorMessage: `Invalid 'alg' value ${alg}. Supported values are ${jarm.serverMetadata.authorization_encryption_alg_values_supported.join(", ")}`
|
|
1634
|
+
});
|
|
1454
1635
|
let additionalJwtPayload;
|
|
1455
1636
|
if (jarm?.jwtSigner) {
|
|
1456
1637
|
if (!jarm.authorizationServer) {
|
|
@@ -1479,13 +1660,13 @@ async function createOpenid4vpAuthorizationResponse(options) {
|
|
|
1479
1660
|
const result = await createJarmAuthorizationResponse({
|
|
1480
1661
|
jarmAuthorizationResponse: jarmResponsePayload,
|
|
1481
1662
|
jwtSigner: jarm?.jwtSigner,
|
|
1482
|
-
jweEncryptor: jarm?.encryption
|
|
1663
|
+
jweEncryptor: jarm?.encryption ? {
|
|
1483
1664
|
method: "jwk",
|
|
1484
|
-
publicJwk:
|
|
1485
|
-
apu: jarm.encryption.nonce ?
|
|
1486
|
-
apv:
|
|
1487
|
-
alg
|
|
1488
|
-
enc
|
|
1665
|
+
publicJwk: encJwk,
|
|
1666
|
+
apu: jarm.encryption.nonce ? encodeToBase64Url2(jarm.encryption.nonce) : void 0,
|
|
1667
|
+
apv: encodeToBase64Url2(authorizationRequestPayload.nonce),
|
|
1668
|
+
alg,
|
|
1669
|
+
enc
|
|
1489
1670
|
} : void 0,
|
|
1490
1671
|
callbacks: {
|
|
1491
1672
|
signJwt: callbacks.signJwt,
|
|
@@ -1678,7 +1859,7 @@ function parseOpenid4VpAuthorizationResponsePayload(payload) {
|
|
|
1678
1859
|
}
|
|
1679
1860
|
|
|
1680
1861
|
// src/authorization-response/parse-jarm-authorization-response.ts
|
|
1681
|
-
import { Oauth2Error as Oauth2Error12, decodeJwtHeader, zCompactJwe as zCompactJwe3, zCompactJwt as zCompactJwt3 } from "@openid4vc/oauth2";
|
|
1862
|
+
import { Oauth2Error as Oauth2Error12, decodeJwtHeader as decodeJwtHeader2, zCompactJwe as zCompactJwe3, zCompactJwt as zCompactJwt3 } from "@openid4vc/oauth2";
|
|
1682
1863
|
import { parseWithErrorHandling as parseWithErrorHandling7 } from "@openid4vc/utils";
|
|
1683
1864
|
import z20 from "zod";
|
|
1684
1865
|
async function parseJarmAuthorizationResponse(options) {
|
|
@@ -1694,7 +1875,7 @@ async function parseJarmAuthorizationResponse(options) {
|
|
|
1694
1875
|
expectedClientId,
|
|
1695
1876
|
authorizationRequestPayload
|
|
1696
1877
|
});
|
|
1697
|
-
const { header: jarmHeader } =
|
|
1878
|
+
const { header: jarmHeader } = decodeJwtHeader2({
|
|
1698
1879
|
jwt: jarmAuthorizationResponseJwt,
|
|
1699
1880
|
headerSchema: zJarmHeader
|
|
1700
1881
|
});
|
|
@@ -1732,9 +1913,7 @@ async function parseOpenid4vpAuthorizationResponse(options) {
|
|
|
1732
1913
|
jarmResponseJwt: authorizationResponse.response,
|
|
1733
1914
|
callbacks,
|
|
1734
1915
|
authorizationRequestPayload,
|
|
1735
|
-
|
|
1736
|
-
// TODO: allow both versions, in case of e.g. did:
|
|
1737
|
-
expectedClientId: expectedClientId.legacyClientId ?? expectedClientId.clientId
|
|
1916
|
+
expectedClientId: expectedClientId.effectiveClientId
|
|
1738
1917
|
});
|
|
1739
1918
|
}
|
|
1740
1919
|
const authorizationResponsePayload = parseOpenid4VpAuthorizationResponsePayload(authorizationResponse);
|
|
@@ -1782,11 +1961,11 @@ var Openid4vpClient = class {
|
|
|
1782
1961
|
|
|
1783
1962
|
// src/transaction-data/verify-transaction-data.ts
|
|
1784
1963
|
import {
|
|
1785
|
-
HashAlgorithm,
|
|
1964
|
+
HashAlgorithm as HashAlgorithm2,
|
|
1786
1965
|
Oauth2ErrorCodes as Oauth2ErrorCodes11,
|
|
1787
1966
|
Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError13
|
|
1788
1967
|
} from "@openid4vc/oauth2";
|
|
1789
|
-
import { decodeUtf8String, encodeToBase64Url as
|
|
1968
|
+
import { decodeUtf8String, encodeToBase64Url as encodeToBase64Url3 } from "@openid4vc/utils";
|
|
1790
1969
|
async function verifyTransactionData(options) {
|
|
1791
1970
|
const parsedTransactionData = parseTransactionData({
|
|
1792
1971
|
transactionData: options.transactionData
|
|
@@ -1809,11 +1988,11 @@ async function verifyTransactionDataEntry({
|
|
|
1809
1988
|
}) {
|
|
1810
1989
|
const allowedAlgs = entry.transactionData.transaction_data_hashes_alg ?? ["sha-256"];
|
|
1811
1990
|
const supportedAlgs = allowedAlgs.filter(
|
|
1812
|
-
(alg) => Object.values(
|
|
1991
|
+
(alg) => Object.values(HashAlgorithm2).includes(alg)
|
|
1813
1992
|
);
|
|
1814
1993
|
const hashes = {};
|
|
1815
1994
|
for (const alg of supportedAlgs) {
|
|
1816
|
-
hashes[alg] =
|
|
1995
|
+
hashes[alg] = encodeToBase64Url3(await callbacks.hash(decodeUtf8String(entry.encoded), alg));
|
|
1817
1996
|
}
|
|
1818
1997
|
for (const credentialId of entry.transactionData.credential_ids) {
|
|
1819
1998
|
const transactionDataHashesCredential = credentials[credentialId];
|
|
@@ -1829,7 +2008,7 @@ async function verifyTransactionDataEntry({
|
|
|
1829
2008
|
if (!hash) {
|
|
1830
2009
|
throw new Oauth2ServerErrorResponseError13({
|
|
1831
2010
|
error: Oauth2ErrorCodes11.InvalidTransactionData,
|
|
1832
|
-
error_description: `Transaction data entry with index ${entry.transactionDataIndex} is hashed using unsupported alg '${alg}'. This library only supports verification of transaction data hashes using alg values ${Object.values(
|
|
2011
|
+
error_description: `Transaction data entry with index ${entry.transactionDataIndex} is hashed using unsupported alg '${alg}'. This library only supports verification of transaction data hashes using alg values ${Object.values(HashAlgorithm2).join(", ")}. Either verify the hashes outside of this library, or limit the allowed alg values to the ones supported by this library.`
|
|
1833
2012
|
});
|
|
1834
2013
|
}
|
|
1835
2014
|
const credentialHashIndex = transactionDataHashesCredential.transaction_data_hashes.indexOf(hash);
|
|
@@ -1885,7 +2064,7 @@ var Openid4vpVerifier = class {
|
|
|
1885
2064
|
|
|
1886
2065
|
// src/models/z-credential-formats.ts
|
|
1887
2066
|
import { z as z21 } from "zod";
|
|
1888
|
-
var zCredentialFormat = z21.enum(["jwt_vc_json", "ldp_vc", "
|
|
2067
|
+
var zCredentialFormat = z21.enum(["jwt_vc_json", "ldp_vc", "mso_mdoc", "dc+sd-jwt", "vc+sd-jwt"]);
|
|
1889
2068
|
|
|
1890
2069
|
// src/models/z-proof-formats.ts
|
|
1891
2070
|
import { z as z22 } from "zod";
|
|
@@ -1895,8 +2074,13 @@ var zProofFormat = z22.enum(["jwt_vp_json", "ldc_vp", "ac_vp", "dc+sd-jwt", "vc+
|
|
|
1895
2074
|
import { z as z23 } from "zod";
|
|
1896
2075
|
var zWalletMetadata = z23.object({
|
|
1897
2076
|
presentation_definition_uri_supported: z23.optional(z23.boolean()),
|
|
1898
|
-
|
|
1899
|
-
|
|
2077
|
+
// Up until draft 26 the legacy format was used
|
|
2078
|
+
vp_formats_supported: z23.optional(zVpFormatsSupported.or(zLegacyVpFormats)),
|
|
2079
|
+
client_id_schemes_supported: z23.optional(
|
|
2080
|
+
// client_id_schemes_supported was from before decentralized_identifier and openid_federation were defined
|
|
2081
|
+
z23.array(zClientIdPrefix.exclude(["decentralized_identifier", "openid_federation"]))
|
|
2082
|
+
),
|
|
2083
|
+
client_id_prefixes_supported: z23.optional(z23.array(zUniformClientIdPrefix)),
|
|
1900
2084
|
request_object_signing_alg_values_supported: z23.optional(z23.array(z23.string())),
|
|
1901
2085
|
authorization_encryption_alg_values_supported: z23.optional(z23.array(z23.string())),
|
|
1902
2086
|
authorization_encryption_enc_values_supported: z23.optional(z23.array(z23.string()))
|
|
@@ -1922,7 +2106,7 @@ export {
|
|
|
1922
2106
|
validateOpenid4vpAuthorizationRequestPayload,
|
|
1923
2107
|
validateOpenid4vpAuthorizationResponsePayload,
|
|
1924
2108
|
verifyJarmAuthorizationResponse,
|
|
1925
|
-
|
|
2109
|
+
zClientIdPrefix,
|
|
1926
2110
|
zClientMetadata,
|
|
1927
2111
|
zCredentialFormat,
|
|
1928
2112
|
zJarmClientMetadata,
|