@openid4vc/openid4vp 0.3.0-alpha-20250320222745 → 0.3.0-alpha-20250321120839
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 +6830 -1196
- package/dist/index.d.ts +6830 -1196
- package/dist/index.js +237 -212
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +219 -194
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/client-identifier-scheme/parse-client-identifier-scheme.ts
|
|
2
|
-
import { Oauth2ErrorCodes
|
|
3
|
-
import { URL as URL2 } from "@openid4vc/utils";
|
|
2
|
+
import { Oauth2ErrorCodes, Oauth2ServerErrorResponseError } from "@openid4vc/oauth2";
|
|
3
|
+
import { URL as URL2, zHttpsUrl as zHttpsUrl3 } from "@openid4vc/utils";
|
|
4
4
|
|
|
5
5
|
// src/authorization-request/z-authorization-request-dc-api.ts
|
|
6
6
|
import { z as z5 } from "zod";
|
|
@@ -139,37 +139,31 @@ var zOpenid4vpAuthorizationRequestFromUriParams = z4.string().url().transform((u
|
|
|
139
139
|
);
|
|
140
140
|
|
|
141
141
|
// src/authorization-request/z-authorization-request-dc-api.ts
|
|
142
|
+
var zOpenid4vpResponseModeDcApi = z5.enum(["dc_api", "dc_api.jwt", "w3c_dc_api.jwt", "w3c_dc_api"]);
|
|
142
143
|
var zOpenid4vpAuthorizationRequestDcApi = zOpenid4vpAuthorizationRequest.pick({
|
|
143
|
-
client_id: true,
|
|
144
144
|
response_type: true,
|
|
145
|
-
response_mode: true,
|
|
146
145
|
nonce: true,
|
|
147
146
|
presentation_definition: true,
|
|
148
147
|
client_metadata: true,
|
|
149
148
|
transaction_data: true,
|
|
150
149
|
dcql_query: true,
|
|
151
|
-
trust_chain: true
|
|
150
|
+
trust_chain: true,
|
|
151
|
+
state: true
|
|
152
152
|
}).extend({
|
|
153
153
|
client_id: z5.optional(z5.string()),
|
|
154
154
|
expected_origins: z5.array(z5.string()).optional(),
|
|
155
|
-
response_mode:
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
"verifier_attestation",
|
|
162
|
-
"x509_san_dns",
|
|
163
|
-
"x509_san_uri"
|
|
164
|
-
]).optional()
|
|
165
|
-
}).strip();
|
|
155
|
+
response_mode: zOpenid4vpResponseModeDcApi,
|
|
156
|
+
// Not allowed with dc_api, but added to make working with interfaces easier
|
|
157
|
+
client_id_scheme: z5.never().optional(),
|
|
158
|
+
scope: z5.never().optional()
|
|
159
|
+
// TODO: should we disallow any properties specifically, such as redirect_uri and response_uri?
|
|
160
|
+
});
|
|
166
161
|
function isOpenid4vpAuthorizationRequestDcApi(request) {
|
|
167
|
-
return request.response_mode
|
|
162
|
+
return request.response_mode !== void 0 && zOpenid4vpResponseModeDcApi.options.includes(
|
|
163
|
+
request.response_mode
|
|
164
|
+
);
|
|
168
165
|
}
|
|
169
166
|
|
|
170
|
-
// src/version.ts
|
|
171
|
-
import { Oauth2ErrorCodes, Oauth2ServerErrorResponseError } from "@openid4vc/oauth2";
|
|
172
|
-
|
|
173
167
|
// src/client-identifier-scheme/z-client-id-scheme.ts
|
|
174
168
|
import { z as z6 } from "zod";
|
|
175
169
|
var zClientIdScheme = z6.enum([
|
|
@@ -182,155 +176,114 @@ var zClientIdScheme = z6.enum([
|
|
|
182
176
|
"x509_san_uri",
|
|
183
177
|
"web-origin"
|
|
184
178
|
]);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
requirements.push([">=", 23]);
|
|
195
|
-
}
|
|
196
|
-
if (isOpenid4vpAuthorizationRequestDcApi(request) && (request.transaction_data || request.dcql_query)) {
|
|
197
|
-
requirements.push([">=", 23]);
|
|
198
|
-
}
|
|
199
|
-
if (request.dcql_query) {
|
|
200
|
-
requirements.push([">=", 22]);
|
|
201
|
-
}
|
|
202
|
-
if (request.transaction_data) {
|
|
203
|
-
requirements.push([">=", 22]);
|
|
204
|
-
}
|
|
205
|
-
if (request.client_id_scheme) {
|
|
206
|
-
requirements.push(["<", 22]);
|
|
207
|
-
}
|
|
208
|
-
if (request.client_id) {
|
|
209
|
-
const colonIndex = request.client_id.indexOf(":");
|
|
210
|
-
const schemePart = request.client_id.substring(0, colonIndex);
|
|
211
|
-
const parsedScheme = zClientIdScheme.safeParse(schemePart);
|
|
212
|
-
if (parsedScheme.success && parsedScheme.data !== "did" && parsedScheme.data !== "https") {
|
|
213
|
-
requirements.push([">=", 22]);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
if (!request.client_id) {
|
|
217
|
-
requirements.push([">=", 21]);
|
|
218
|
-
}
|
|
219
|
-
if ("client_metadata_uri" in request) {
|
|
220
|
-
requirements.push(["<", 21]);
|
|
221
|
-
}
|
|
222
|
-
if (isOpenid4vpAuthorizationRequestDcApi(request)) {
|
|
223
|
-
requirements.push([">=", 21]);
|
|
224
|
-
}
|
|
225
|
-
if ("request_uri_method" in request || "wallet_nonce" in request) {
|
|
226
|
-
requirements.push([">=", 21]);
|
|
227
|
-
}
|
|
228
|
-
if (request.client_id_scheme === "verifier_attestation") {
|
|
229
|
-
requirements.push([">=", 20]);
|
|
230
|
-
}
|
|
231
|
-
if (request.client_id_scheme === "x509_san_dns" || request.client_id_scheme === "x509_san_uri") {
|
|
232
|
-
requirements.push([">=", 19]);
|
|
233
|
-
}
|
|
234
|
-
const lessThanVersions = requirements.filter(([operator]) => operator === "<").map(([_, version]) => version);
|
|
235
|
-
const greaterThanVersions = requirements.filter(([operator]) => operator === ">=").map(([_, version]) => version);
|
|
236
|
-
const highestPossibleVersion = lessThanVersions.length > 0 ? Math.max(Math.min(...lessThanVersions) - 1, 18) : 24;
|
|
237
|
-
const lowestRequiredVersion = greaterThanVersions.length > 0 ? Math.max(...greaterThanVersions) : 18;
|
|
238
|
-
if (lowestRequiredVersion > highestPossibleVersion) {
|
|
239
|
-
throw new Oauth2ServerErrorResponseError({
|
|
240
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
241
|
-
error_description: "Could not infer openid4vp version from the openid4vp request payload."
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
return highestPossibleVersion;
|
|
245
|
-
}
|
|
179
|
+
var zLegacyClientIdScheme = z6.enum([
|
|
180
|
+
"pre-registered",
|
|
181
|
+
"redirect_uri",
|
|
182
|
+
"entity_id",
|
|
183
|
+
"did",
|
|
184
|
+
"verifier_attestation",
|
|
185
|
+
"x509_san_dns",
|
|
186
|
+
"x509_san_uri"
|
|
187
|
+
]);
|
|
246
188
|
|
|
247
189
|
// src/client-identifier-scheme/parse-client-identifier-scheme.ts
|
|
248
190
|
function getOpenid4vpClientId(options) {
|
|
249
|
-
const version = parseAuthorizationRequestVersion(options.authorizationRequestPayload);
|
|
250
|
-
if (version < 22) {
|
|
251
|
-
return getLegacyClientId(options);
|
|
252
|
-
}
|
|
253
191
|
if (isOpenid4vpAuthorizationRequestDcApi(options.authorizationRequestPayload)) {
|
|
254
192
|
if (!options.origin) {
|
|
255
|
-
throw new
|
|
256
|
-
error:
|
|
193
|
+
throw new Oauth2ServerErrorResponseError({
|
|
194
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
257
195
|
error_description: "Failed to parse client identifier. 'origin' is required for requests with response_mode 'dc_api' and 'dc_api.jwt'"
|
|
258
196
|
});
|
|
259
197
|
}
|
|
260
|
-
|
|
261
|
-
|
|
198
|
+
return {
|
|
199
|
+
clientId: options.authorizationRequestPayload.client_id ?? `web-origin:${options.origin}`
|
|
200
|
+
};
|
|
262
201
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
202
|
+
if (!options.authorizationRequestPayload.client_id) {
|
|
203
|
+
throw new Oauth2ServerErrorResponseError({
|
|
204
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
205
|
+
error_description: `Failed to parse client identifier. Missing required client_id parameter for response_mode '${options.authorizationRequestPayload.response_mode}'.`
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
if (options.authorizationRequestPayload.client_id_scheme) {
|
|
209
|
+
const parsedClientIdScheme = zLegacyClientIdScheme.safeParse(options.authorizationRequestPayload.client_id_scheme);
|
|
210
|
+
if (!parsedClientIdScheme.success) {
|
|
211
|
+
throw new Oauth2ServerErrorResponseError({
|
|
212
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
213
|
+
error_description: `Failed to parse client identifier. Unsupported client_id_scheme value '${options.authorizationRequestPayload.client_id_scheme}'.`
|
|
273
214
|
});
|
|
274
215
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
return
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
216
|
+
const clientIdScheme = parsedClientIdScheme.data === "entity_id" ? "https" : parsedClientIdScheme.data;
|
|
217
|
+
if (clientIdScheme === "https" || clientIdScheme === "did" || clientIdScheme === "pre-registered") {
|
|
218
|
+
return { clientId: options.authorizationRequestPayload.client_id };
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
clientId: `${clientIdScheme}:${options.authorizationRequestPayload.client_id}`,
|
|
222
|
+
legacyClientId: options.authorizationRequestPayload.client_id
|
|
223
|
+
};
|
|
283
224
|
}
|
|
284
|
-
return
|
|
225
|
+
return {
|
|
226
|
+
clientId: options.authorizationRequestPayload.client_id
|
|
227
|
+
};
|
|
285
228
|
}
|
|
286
229
|
function parseClientIdentifier(options, parserConfig) {
|
|
287
|
-
const { authorizationRequestPayload, jar } = options;
|
|
230
|
+
const { authorizationRequestPayload, jar, origin } = options;
|
|
288
231
|
const parserConfigWithDefaults = {
|
|
289
232
|
supportedSchemes: parserConfig?.supportedSchemes || Object.values(zClientIdScheme.options)
|
|
290
233
|
};
|
|
291
|
-
const clientId = getOpenid4vpClientId(
|
|
234
|
+
const { clientId, legacyClientId } = getOpenid4vpClientId({
|
|
235
|
+
authorizationRequestPayload,
|
|
236
|
+
origin
|
|
237
|
+
});
|
|
292
238
|
const colonIndex = clientId.indexOf(":");
|
|
293
239
|
if (colonIndex === -1) {
|
|
294
240
|
return {
|
|
295
241
|
scheme: "pre-registered",
|
|
296
242
|
identifier: clientId,
|
|
297
243
|
originalValue: clientId,
|
|
244
|
+
legacyClientId,
|
|
298
245
|
clientMetadata: authorizationRequestPayload.client_metadata
|
|
299
246
|
};
|
|
300
247
|
}
|
|
301
248
|
const schemePart = clientId.substring(0, colonIndex);
|
|
302
249
|
const identifierPart = clientId.substring(colonIndex + 1);
|
|
303
250
|
if (!parserConfigWithDefaults.supportedSchemes.includes(schemePart)) {
|
|
304
|
-
throw new
|
|
305
|
-
error:
|
|
251
|
+
throw new Oauth2ServerErrorResponseError({
|
|
252
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
306
253
|
error_description: `Unsupported client identifier scheme. ${schemePart} is not supported.`
|
|
307
254
|
});
|
|
308
255
|
}
|
|
309
256
|
const scheme = schemePart;
|
|
310
257
|
if (scheme === "https") {
|
|
311
|
-
if (!
|
|
312
|
-
throw new
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
258
|
+
if (!zHttpsUrl3.safeParse(clientId).success) {
|
|
259
|
+
throw new Oauth2ServerErrorResponseError(
|
|
260
|
+
{
|
|
261
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
262
|
+
error_description: "Invalid client identifier. Client identifier must start with https://"
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
internalMessage: `Insecure http:// urls can be enabled by setting the 'allowInsecureUrls' option using setGlobalConfig`
|
|
266
|
+
}
|
|
267
|
+
);
|
|
316
268
|
}
|
|
317
269
|
return {
|
|
318
270
|
scheme,
|
|
319
271
|
identifier: clientId,
|
|
320
272
|
originalValue: clientId,
|
|
273
|
+
legacyClientId,
|
|
321
274
|
trustChain: authorizationRequestPayload.trust_chain
|
|
322
275
|
};
|
|
323
276
|
}
|
|
324
277
|
if (scheme === "redirect_uri") {
|
|
325
278
|
if (jar) {
|
|
326
|
-
throw new
|
|
327
|
-
error:
|
|
279
|
+
throw new Oauth2ServerErrorResponseError({
|
|
280
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
328
281
|
error_description: 'Using client identifier scheme "redirect_uri" the request MUST NOT be signed.'
|
|
329
282
|
});
|
|
330
283
|
}
|
|
331
284
|
if (isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
|
|
332
|
-
throw new
|
|
333
|
-
error:
|
|
285
|
+
throw new Oauth2ServerErrorResponseError({
|
|
286
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
334
287
|
error_description: `The client identifier scheme 'redirect_uri' is not supported when using the dc_api response mode.`
|
|
335
288
|
});
|
|
336
289
|
}
|
|
@@ -338,31 +291,32 @@ function parseClientIdentifier(options, parserConfig) {
|
|
|
338
291
|
scheme,
|
|
339
292
|
identifier: identifierPart,
|
|
340
293
|
originalValue: clientId,
|
|
294
|
+
legacyClientId,
|
|
341
295
|
redirectUri: authorizationRequestPayload.redirect_uri ?? authorizationRequestPayload.response_uri
|
|
342
296
|
};
|
|
343
297
|
}
|
|
344
298
|
if (scheme === "did") {
|
|
345
299
|
if (!jar) {
|
|
346
|
-
throw new
|
|
347
|
-
error:
|
|
300
|
+
throw new Oauth2ServerErrorResponseError({
|
|
301
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
348
302
|
error_description: 'Using client identifier scheme "did" requires a signed JAR request.'
|
|
349
303
|
});
|
|
350
304
|
}
|
|
351
305
|
if (!clientId.startsWith("did:")) {
|
|
352
|
-
throw new
|
|
353
|
-
error:
|
|
306
|
+
throw new Oauth2ServerErrorResponseError({
|
|
307
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
354
308
|
error_description: "Invalid client identifier. Client identifier must start with 'did:'"
|
|
355
309
|
});
|
|
356
310
|
}
|
|
357
311
|
if (!jar.signer.publicJwk.kid) {
|
|
358
|
-
throw new
|
|
359
|
-
error:
|
|
312
|
+
throw new Oauth2ServerErrorResponseError({
|
|
313
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
360
314
|
error_description: `Missing required 'kid' for client identifier scheme: did`
|
|
361
315
|
});
|
|
362
316
|
}
|
|
363
317
|
if (!jar.signer.publicJwk.kid?.startsWith(clientId)) {
|
|
364
|
-
throw new
|
|
365
|
-
error:
|
|
318
|
+
throw new Oauth2ServerErrorResponseError({
|
|
319
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
366
320
|
error_description: 'With client identifier scheme "did" the JAR request must be signed by the same DID as the client identifier.'
|
|
367
321
|
});
|
|
368
322
|
}
|
|
@@ -370,27 +324,28 @@ function parseClientIdentifier(options, parserConfig) {
|
|
|
370
324
|
scheme,
|
|
371
325
|
identifier: clientId,
|
|
372
326
|
originalValue: clientId,
|
|
327
|
+
legacyClientId,
|
|
373
328
|
didUrl: jar.signer.publicJwk.kid
|
|
374
329
|
};
|
|
375
330
|
}
|
|
376
331
|
if (scheme === "x509_san_dns" || scheme === "x509_san_uri") {
|
|
377
332
|
if (!jar) {
|
|
378
|
-
throw new
|
|
379
|
-
error:
|
|
333
|
+
throw new Oauth2ServerErrorResponseError({
|
|
334
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
380
335
|
error_description: 'Using client identifier scheme "x509_san_dns" or "x509_san_uri" requires a signed JAR request.'
|
|
381
336
|
});
|
|
382
337
|
}
|
|
383
338
|
if (jar.signer.method !== "x5c") {
|
|
384
|
-
throw new
|
|
385
|
-
error:
|
|
339
|
+
throw new Oauth2ServerErrorResponseError({
|
|
340
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
386
341
|
error_description: "Something went wrong. The JWT signer method is not x5c but the client identifier scheme is x509_san_dns."
|
|
387
342
|
});
|
|
388
343
|
}
|
|
389
344
|
if (scheme === "x509_san_dns") {
|
|
390
345
|
if (!options.callbacks.getX509CertificateMetadata) {
|
|
391
|
-
throw new
|
|
346
|
+
throw new Oauth2ServerErrorResponseError(
|
|
392
347
|
{
|
|
393
|
-
error:
|
|
348
|
+
error: Oauth2ErrorCodes.ServerError
|
|
394
349
|
},
|
|
395
350
|
{
|
|
396
351
|
internalMessage: "Missing required 'getX509CertificateMetadata' callback for verification of 'x509_san_dns' client id scheme"
|
|
@@ -399,25 +354,25 @@ function parseClientIdentifier(options, parserConfig) {
|
|
|
399
354
|
}
|
|
400
355
|
const { sanDnsNames } = options.callbacks.getX509CertificateMetadata(jar.signer.x5c[0]);
|
|
401
356
|
if (!sanDnsNames.includes(identifierPart)) {
|
|
402
|
-
throw new
|
|
403
|
-
error:
|
|
357
|
+
throw new Oauth2ServerErrorResponseError({
|
|
358
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
404
359
|
error_description: `Invalid client identifier. One of the leaf certificates san dns names [${sanDnsNames.join(", ")}] must match the client identifier '${identifierPart}'. `
|
|
405
360
|
});
|
|
406
361
|
}
|
|
407
362
|
if (!isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
|
|
408
363
|
const uri = authorizationRequestPayload.redirect_uri ?? authorizationRequestPayload.response_uri;
|
|
409
364
|
if (!uri || new URL2(uri).hostname !== identifierPart) {
|
|
410
|
-
throw new
|
|
411
|
-
error:
|
|
365
|
+
throw new Oauth2ServerErrorResponseError({
|
|
366
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
412
367
|
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."
|
|
413
368
|
});
|
|
414
369
|
}
|
|
415
370
|
}
|
|
416
371
|
} else if (scheme === "x509_san_uri") {
|
|
417
372
|
if (!options.callbacks.getX509CertificateMetadata) {
|
|
418
|
-
throw new
|
|
373
|
+
throw new Oauth2ServerErrorResponseError(
|
|
419
374
|
{
|
|
420
|
-
error:
|
|
375
|
+
error: Oauth2ErrorCodes.ServerError
|
|
421
376
|
},
|
|
422
377
|
{
|
|
423
378
|
internalMessage: "Missing required 'getX509CertificateMetadata' callback for verification of 'x509_san_uri' client id scheme"
|
|
@@ -426,16 +381,16 @@ function parseClientIdentifier(options, parserConfig) {
|
|
|
426
381
|
}
|
|
427
382
|
const { sanUriNames } = options.callbacks.getX509CertificateMetadata(jar.signer.x5c[0]);
|
|
428
383
|
if (!sanUriNames.includes(identifierPart)) {
|
|
429
|
-
throw new
|
|
430
|
-
error:
|
|
384
|
+
throw new Oauth2ServerErrorResponseError({
|
|
385
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
431
386
|
error_description: `Invalid client identifier. One of the leaf certificates san uri names [${sanUriNames.join(", ")}] must match the client identifier '${identifierPart}'.`
|
|
432
387
|
});
|
|
433
388
|
}
|
|
434
389
|
if (!isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
|
|
435
390
|
const uri = authorizationRequestPayload.redirect_uri || authorizationRequestPayload.response_uri;
|
|
436
391
|
if (!uri || uri !== identifierPart) {
|
|
437
|
-
throw new
|
|
438
|
-
error:
|
|
392
|
+
throw new Oauth2ServerErrorResponseError({
|
|
393
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
439
394
|
error_description: "The redirect_uri value MUST match the Client Identifier without the prefix x509_san_uri"
|
|
440
395
|
});
|
|
441
396
|
}
|
|
@@ -445,6 +400,7 @@ function parseClientIdentifier(options, parserConfig) {
|
|
|
445
400
|
scheme,
|
|
446
401
|
identifier: identifierPart,
|
|
447
402
|
originalValue: clientId,
|
|
403
|
+
legacyClientId,
|
|
448
404
|
x5c: jar.signer.x5c
|
|
449
405
|
};
|
|
450
406
|
}
|
|
@@ -453,13 +409,14 @@ function parseClientIdentifier(options, parserConfig) {
|
|
|
453
409
|
scheme,
|
|
454
410
|
identifier: identifierPart,
|
|
455
411
|
originalValue: clientId,
|
|
412
|
+
legacyClientId,
|
|
456
413
|
clientMetadata: authorizationRequestPayload.client_metadata
|
|
457
414
|
};
|
|
458
415
|
}
|
|
459
416
|
if (scheme === "verifier_attestation") {
|
|
460
417
|
if (!jar) {
|
|
461
|
-
throw new
|
|
462
|
-
error:
|
|
418
|
+
throw new Oauth2ServerErrorResponseError({
|
|
419
|
+
error: Oauth2ErrorCodes.InvalidRequest,
|
|
463
420
|
error_description: 'Using client identifier scheme "verifier_attestation" requires a signed JAR request.'
|
|
464
421
|
});
|
|
465
422
|
}
|
|
@@ -467,6 +424,7 @@ function parseClientIdentifier(options, parserConfig) {
|
|
|
467
424
|
return {
|
|
468
425
|
scheme,
|
|
469
426
|
identifier: identifierPart,
|
|
427
|
+
legacyClientId,
|
|
470
428
|
originalValue: clientId
|
|
471
429
|
};
|
|
472
430
|
}
|
|
@@ -622,94 +580,94 @@ async function createJarAuthorizationRequest(options) {
|
|
|
622
580
|
}
|
|
623
581
|
|
|
624
582
|
// src/authorization-request/validate-authorization-request.ts
|
|
625
|
-
import { Oauth2ErrorCodes as
|
|
626
|
-
import { zHttpsUrl as
|
|
583
|
+
import { Oauth2ErrorCodes as Oauth2ErrorCodes2, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError2 } from "@openid4vc/oauth2";
|
|
584
|
+
import { zHttpsUrl as zHttpsUrl4 } from "@openid4vc/utils";
|
|
627
585
|
var validateOpenid4vpAuthorizationRequestPayload = (options) => {
|
|
628
586
|
const { params, walletVerificationOptions } = options;
|
|
629
587
|
if (!params.redirect_uri && !params.response_uri) {
|
|
630
|
-
throw new
|
|
631
|
-
error:
|
|
588
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
589
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
632
590
|
error_description: `Missing required 'redirect_uri' or 'response_uri' in openid4vp authorization request.`
|
|
633
591
|
});
|
|
634
592
|
}
|
|
635
593
|
if (params.response_uri && !["direct_post", "direct_post.jwt"].find((mode) => mode === params.response_mode)) {
|
|
636
|
-
throw new
|
|
637
|
-
error:
|
|
594
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
595
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
638
596
|
error_description: `The 'response_mode' parameter MUST be 'direct_post' or 'direct_post.jwt' when 'response_uri' is provided. Current: ${params.response_mode}`
|
|
639
597
|
});
|
|
640
598
|
}
|
|
641
599
|
if ([params.presentation_definition_uri, params.presentation_definition, params.dcql_query, params.scope].filter(
|
|
642
600
|
Boolean
|
|
643
601
|
).length > 1) {
|
|
644
|
-
throw new
|
|
645
|
-
error:
|
|
602
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
603
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
646
604
|
error_description: "Exactly one of the following parameters MUST be present in the authorization request: dcql_query, presentation_definition, presentation_definition_uri, or a scope value representing a Presentation Definition."
|
|
647
605
|
});
|
|
648
606
|
}
|
|
649
607
|
if (params.request_uri_method && !params.request_uri) {
|
|
650
|
-
throw new
|
|
651
|
-
error:
|
|
608
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
609
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
652
610
|
error_description: 'The "request_uri_method" parameter MUST NOT be present in the authorization request if the "request_uri" parameter is not present.'
|
|
653
611
|
});
|
|
654
612
|
}
|
|
655
613
|
if (params.request_uri_method && !["GET", "POST"].includes(params.request_uri_method)) {
|
|
656
|
-
throw new
|
|
657
|
-
error:
|
|
614
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
615
|
+
error: Oauth2ErrorCodes2.InvalidRequestUriMethod,
|
|
658
616
|
error_description: `The 'request_uri_method' parameter MUST be 'GET' or 'POST'. Current: ${params.request_uri_method}`
|
|
659
617
|
});
|
|
660
618
|
}
|
|
661
|
-
if (params.trust_chain && !
|
|
662
|
-
throw new
|
|
663
|
-
error:
|
|
619
|
+
if (params.trust_chain && !zHttpsUrl4.safeParse(params.client_id).success) {
|
|
620
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
621
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
664
622
|
error_description: 'The "trust_chain" parameter MUST NOT be present in the authorization request if the "client_id" is not an OpenId Federation Entity Identifier starting with http:// or https://.'
|
|
665
623
|
});
|
|
666
624
|
}
|
|
667
625
|
if (walletVerificationOptions?.expectedNonce && !params.wallet_nonce) {
|
|
668
|
-
throw new
|
|
669
|
-
error:
|
|
626
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
627
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
670
628
|
error_description: 'The "wallet_nonce" parameter MUST be present in the authorization request when the "expectedNonce" parameter is provided.'
|
|
671
629
|
});
|
|
672
630
|
}
|
|
673
631
|
if (walletVerificationOptions?.expectedNonce !== params.wallet_nonce) {
|
|
674
|
-
throw new
|
|
675
|
-
error:
|
|
632
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
633
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
676
634
|
error_description: 'The "wallet_nonce" parameter MUST match the "expectedNonce" parameter when the "expectedNonce" parameter is provided.'
|
|
677
635
|
});
|
|
678
636
|
}
|
|
679
637
|
if (params.client_id.startsWith("web-origin:")) {
|
|
680
|
-
throw new
|
|
681
|
-
error:
|
|
638
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
639
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
682
640
|
error_description: `The 'client_id' parameter MUST NOT use client identifier scheme 'web-origin' when not using the dc_api response mode. Current: ${params.client_id}`
|
|
683
641
|
});
|
|
684
642
|
}
|
|
685
643
|
};
|
|
686
644
|
|
|
687
645
|
// src/authorization-request/validate-authorization-request-dc-api.ts
|
|
688
|
-
import { Oauth2ErrorCodes as
|
|
646
|
+
import { Oauth2ErrorCodes as Oauth2ErrorCodes3, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError3 } from "@openid4vc/oauth2";
|
|
689
647
|
var validateOpenid4vpAuthorizationRequestDcApiPayload = (options) => {
|
|
690
648
|
const { params, isJarRequest, disableOriginValidation, origin } = options;
|
|
691
649
|
if (isJarRequest && !params.expected_origins) {
|
|
692
|
-
throw new
|
|
693
|
-
error:
|
|
650
|
+
throw new Oauth2ServerErrorResponseError3({
|
|
651
|
+
error: Oauth2ErrorCodes3.InvalidRequest,
|
|
694
652
|
error_description: `The 'expected_origins' parameter MUST be present when using the dc_api response mode in combinaction with jar.`
|
|
695
653
|
});
|
|
696
654
|
}
|
|
697
655
|
if ([params.presentation_definition, params.dcql_query].filter(Boolean).length !== 1) {
|
|
698
|
-
throw new
|
|
699
|
-
error:
|
|
656
|
+
throw new Oauth2ServerErrorResponseError3({
|
|
657
|
+
error: Oauth2ErrorCodes3.InvalidRequest,
|
|
700
658
|
error_description: "Exactly one of the following parameters MUST be present in the Authorization Request: dcql_query or presentation_definition"
|
|
701
659
|
});
|
|
702
660
|
}
|
|
703
661
|
if (params.expected_origins && !disableOriginValidation) {
|
|
704
662
|
if (!origin) {
|
|
705
|
-
throw new
|
|
706
|
-
error:
|
|
663
|
+
throw new Oauth2ServerErrorResponseError3({
|
|
664
|
+
error: Oauth2ErrorCodes3.InvalidRequest,
|
|
707
665
|
error_description: `Failed to validate the 'origin' of the authorization request. The 'origin' was not provided.`
|
|
708
666
|
});
|
|
709
667
|
}
|
|
710
668
|
if (params.expected_origins && !params.expected_origins.includes(origin)) {
|
|
711
|
-
throw new
|
|
712
|
-
error:
|
|
669
|
+
throw new Oauth2ServerErrorResponseError3({
|
|
670
|
+
error: Oauth2ErrorCodes3.InvalidRequest,
|
|
713
671
|
error_description: `The 'expected_origins' parameter MUST include the origin of the authorization request. Current: ${params.expected_origins.join(", ")}`
|
|
714
672
|
});
|
|
715
673
|
}
|
|
@@ -789,25 +747,25 @@ import { parseWithErrorHandling as parseWithErrorHandling3 } from "@openid4vc/ut
|
|
|
789
747
|
import z10 from "zod";
|
|
790
748
|
|
|
791
749
|
// src/jar/z-jar-authorization-request.ts
|
|
792
|
-
import { Oauth2ServerErrorResponseError as
|
|
793
|
-
import { zHttpsUrl as
|
|
750
|
+
import { Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError4 } from "@openid4vc/oauth2";
|
|
751
|
+
import { zHttpsUrl as zHttpsUrl5 } from "@openid4vc/utils";
|
|
794
752
|
import { z as z9 } from "zod";
|
|
795
753
|
var zJarAuthorizationRequest = z9.object({
|
|
796
754
|
request: z9.optional(z9.string()),
|
|
797
|
-
request_uri: z9.optional(
|
|
755
|
+
request_uri: z9.optional(zHttpsUrl5),
|
|
798
756
|
request_uri_method: z9.optional(z9.string()),
|
|
799
757
|
client_id: z9.optional(z9.string())
|
|
800
758
|
}).passthrough();
|
|
801
759
|
function validateJarRequestParams(options) {
|
|
802
760
|
const { jarRequestParams } = options;
|
|
803
761
|
if (jarRequestParams.request && jarRequestParams.request_uri) {
|
|
804
|
-
throw new
|
|
762
|
+
throw new Oauth2ServerErrorResponseError4({
|
|
805
763
|
error: "invalid_request_object",
|
|
806
764
|
error_description: "request and request_uri cannot both be present in a JAR request"
|
|
807
765
|
});
|
|
808
766
|
}
|
|
809
767
|
if (!jarRequestParams.request && !jarRequestParams.request_uri) {
|
|
810
|
-
throw new
|
|
768
|
+
throw new Oauth2ServerErrorResponseError4({
|
|
811
769
|
error: "invalid_request_object",
|
|
812
770
|
error_description: "request or request_uri must be present"
|
|
813
771
|
});
|
|
@@ -870,7 +828,7 @@ import { parseWithErrorHandling as parseWithErrorHandling4 } from "@openid4vc/ut
|
|
|
870
828
|
import z14 from "zod";
|
|
871
829
|
|
|
872
830
|
// src/fetch-client-metadata.ts
|
|
873
|
-
import { Oauth2ErrorCodes as
|
|
831
|
+
import { Oauth2ErrorCodes as Oauth2ErrorCodes4, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError5 } from "@openid4vc/oauth2";
|
|
874
832
|
import { ContentType, createZodFetcher } from "@openid4vc/utils";
|
|
875
833
|
async function fetchClientMetadata(options) {
|
|
876
834
|
const { fetch, clientMetadataUri } = options;
|
|
@@ -882,15 +840,15 @@ async function fetchClientMetadata(options) {
|
|
|
882
840
|
}
|
|
883
841
|
});
|
|
884
842
|
if (!response.ok) {
|
|
885
|
-
throw new
|
|
843
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
886
844
|
error_description: `Fetching client metadata from '${clientMetadataUri}' failed with status code '${response.status}'.`,
|
|
887
|
-
error:
|
|
845
|
+
error: Oauth2ErrorCodes4.InvalidRequestUri
|
|
888
846
|
});
|
|
889
847
|
}
|
|
890
848
|
if (!result || !result.success) {
|
|
891
|
-
throw new
|
|
849
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
892
850
|
error_description: `Parsing client metadata from '${clientMetadataUri}' failed.`,
|
|
893
|
-
error:
|
|
851
|
+
error: Oauth2ErrorCodes4.InvalidRequestObject
|
|
894
852
|
});
|
|
895
853
|
}
|
|
896
854
|
return result.data;
|
|
@@ -907,6 +865,68 @@ import {
|
|
|
907
865
|
zCompactJwt as zCompactJwt2
|
|
908
866
|
} from "@openid4vc/oauth2";
|
|
909
867
|
|
|
868
|
+
// src/version.ts
|
|
869
|
+
import { Oauth2ErrorCodes as Oauth2ErrorCodes5, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError6 } from "@openid4vc/oauth2";
|
|
870
|
+
function parseAuthorizationRequestVersion(request) {
|
|
871
|
+
const requirements = [];
|
|
872
|
+
if (isOpenid4vpAuthorizationRequestDcApi(request) && (request.response_mode === "w3c_dc_api" || request.response_mode === "w3c_dc_api.jwt")) {
|
|
873
|
+
requirements.push(["<", 23]);
|
|
874
|
+
requirements.push([">=", 21]);
|
|
875
|
+
}
|
|
876
|
+
if (isOpenid4vpAuthorizationRequestDcApi(request) && request.response_mode === "dc_api" || request.response_mode === "dc_api.jwt") {
|
|
877
|
+
requirements.push([">=", 23]);
|
|
878
|
+
}
|
|
879
|
+
if (isOpenid4vpAuthorizationRequestDcApi(request) && (request.transaction_data || request.dcql_query)) {
|
|
880
|
+
requirements.push([">=", 23]);
|
|
881
|
+
}
|
|
882
|
+
if (request.dcql_query) {
|
|
883
|
+
requirements.push([">=", 22]);
|
|
884
|
+
}
|
|
885
|
+
if (request.transaction_data) {
|
|
886
|
+
requirements.push([">=", 22]);
|
|
887
|
+
}
|
|
888
|
+
if (request.client_id_scheme) {
|
|
889
|
+
requirements.push(["<", 22]);
|
|
890
|
+
}
|
|
891
|
+
if (request.client_id) {
|
|
892
|
+
const colonIndex = request.client_id.indexOf(":");
|
|
893
|
+
const schemePart = request.client_id.substring(0, colonIndex);
|
|
894
|
+
const parsedScheme = zClientIdScheme.safeParse(schemePart);
|
|
895
|
+
if (parsedScheme.success && parsedScheme.data !== "did" && parsedScheme.data !== "https") {
|
|
896
|
+
requirements.push([">=", 22]);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
if (!request.client_id) {
|
|
900
|
+
requirements.push([">=", 21]);
|
|
901
|
+
}
|
|
902
|
+
if (request.client_metadata_uri) {
|
|
903
|
+
requirements.push(["<", 21]);
|
|
904
|
+
}
|
|
905
|
+
if (isOpenid4vpAuthorizationRequestDcApi(request)) {
|
|
906
|
+
requirements.push([">=", 21]);
|
|
907
|
+
}
|
|
908
|
+
if (request.request_uri_method || request.wallet_nonce) {
|
|
909
|
+
requirements.push([">=", 21]);
|
|
910
|
+
}
|
|
911
|
+
if (request.client_id_scheme === "verifier_attestation") {
|
|
912
|
+
requirements.push([">=", 20]);
|
|
913
|
+
}
|
|
914
|
+
if (request.client_id_scheme === "x509_san_dns" || request.client_id_scheme === "x509_san_uri") {
|
|
915
|
+
requirements.push([">=", 19]);
|
|
916
|
+
}
|
|
917
|
+
const lessThanVersions = requirements.filter(([operator]) => operator === "<").map(([_, version]) => version);
|
|
918
|
+
const greaterThanVersions = requirements.filter(([operator]) => operator === ">=").map(([_, version]) => version);
|
|
919
|
+
const highestPossibleVersion = lessThanVersions.length > 0 ? Math.max(Math.min(...lessThanVersions) - 1, 18) : 24;
|
|
920
|
+
const lowestRequiredVersion = greaterThanVersions.length > 0 ? Math.max(...greaterThanVersions) : 18;
|
|
921
|
+
if (lowestRequiredVersion > highestPossibleVersion) {
|
|
922
|
+
throw new Oauth2ServerErrorResponseError6({
|
|
923
|
+
error: Oauth2ErrorCodes5.InvalidRequest,
|
|
924
|
+
error_description: "Could not infer openid4vp version from the openid4vp request payload."
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
return highestPossibleVersion;
|
|
928
|
+
}
|
|
929
|
+
|
|
910
930
|
// src/jar/jar-request-object/fetch-jar-request-object.ts
|
|
911
931
|
import { Oauth2ErrorCodes as Oauth2ErrorCodes6, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError7 } from "@openid4vc/oauth2";
|
|
912
932
|
import { ContentType as ContentType2, createZodFetcher as createZodFetcher2, objectToQueryParams as objectToQueryParams2 } from "@openid4vc/utils";
|
|
@@ -955,7 +975,7 @@ async function verifyJarRequest(options) {
|
|
|
955
975
|
const { callbacks, wallet = {} } = options;
|
|
956
976
|
const jarRequestParams = validateJarRequestParams(options);
|
|
957
977
|
const sendBy = jarRequestParams.request ? "value" : "reference";
|
|
958
|
-
const clientIdentifierScheme = jarRequestParams.client_id ? zClientIdScheme.
|
|
978
|
+
const clientIdentifierScheme = jarRequestParams.client_id ? zClientIdScheme.safeParse(jarRequestParams.client_id.split(":")[0]).data : "web-origin";
|
|
959
979
|
const method = jarRequestParams.request_uri_method ?? "GET";
|
|
960
980
|
if (method !== "GET" && method !== "POST") {
|
|
961
981
|
throw new Oauth2ServerErrorResponseError8({
|
|
@@ -1263,7 +1283,7 @@ async function createOpenid4vpAuthorizationResponse(options) {
|
|
|
1263
1283
|
const { authorizationRequestPayload, jarm, callbacks } = options;
|
|
1264
1284
|
const authorizationResponsePayload = {
|
|
1265
1285
|
...options.authorizationResponsePayload,
|
|
1266
|
-
|
|
1286
|
+
state: authorizationRequestPayload.state
|
|
1267
1287
|
};
|
|
1268
1288
|
if (authorizationRequestPayload.response_mode && isJarmResponseMode(authorizationRequestPayload.response_mode) && !jarm) {
|
|
1269
1289
|
throw new Oauth2Error7(
|
|
@@ -1447,7 +1467,7 @@ function parseDcqlVpToken(vpToken) {
|
|
|
1447
1467
|
// src/authorization-response/validate-authorization-response.ts
|
|
1448
1468
|
function validateOpenid4vpAuthorizationResponsePayload(options) {
|
|
1449
1469
|
const { authorizationRequestPayload, authorizationResponsePayload } = options;
|
|
1450
|
-
if (
|
|
1470
|
+
if (authorizationRequestPayload.state && authorizationRequestPayload.state !== authorizationResponsePayload.state) {
|
|
1451
1471
|
throw new Oauth2Error10("OpenId4Vp Authorization Response state mismatch.");
|
|
1452
1472
|
}
|
|
1453
1473
|
if (authorizationResponsePayload.id_token) {
|
|
@@ -1459,7 +1479,7 @@ function validateOpenid4vpAuthorizationResponsePayload(options) {
|
|
|
1459
1479
|
}
|
|
1460
1480
|
return {
|
|
1461
1481
|
type: "pex",
|
|
1462
|
-
pex:
|
|
1482
|
+
pex: authorizationRequestPayload.scope ? {
|
|
1463
1483
|
scope: authorizationRequestPayload.scope,
|
|
1464
1484
|
presentationSubmission: authorizationResponsePayload.presentation_submission,
|
|
1465
1485
|
presentations: parsePexVpToken(authorizationResponsePayload.vp_token)
|
|
@@ -1474,7 +1494,7 @@ function validateOpenid4vpAuthorizationResponsePayload(options) {
|
|
|
1474
1494
|
const presentations = parseDcqlVpToken(authorizationResponsePayload.vp_token);
|
|
1475
1495
|
return {
|
|
1476
1496
|
type: "dcql",
|
|
1477
|
-
dcql:
|
|
1497
|
+
dcql: authorizationRequestPayload.scope ? {
|
|
1478
1498
|
scope: authorizationRequestPayload.scope,
|
|
1479
1499
|
presentations
|
|
1480
1500
|
} : {
|
|
@@ -1568,13 +1588,18 @@ async function parseJarmAuthorizationResponse(options) {
|
|
|
1568
1588
|
// src/authorization-response/parse-authorization-response.ts
|
|
1569
1589
|
async function parseOpenid4vpAuthorizationResponse(options) {
|
|
1570
1590
|
const { authorizationResponse, callbacks, authorizationRequestPayload, origin } = options;
|
|
1571
|
-
const expectedClientId = getOpenid4vpClientId({
|
|
1591
|
+
const expectedClientId = getOpenid4vpClientId({
|
|
1592
|
+
origin,
|
|
1593
|
+
authorizationRequestPayload
|
|
1594
|
+
});
|
|
1572
1595
|
if (authorizationResponse.response) {
|
|
1573
1596
|
return parseJarmAuthorizationResponse({
|
|
1574
1597
|
jarmResponseJwt: authorizationResponse.response,
|
|
1575
1598
|
callbacks,
|
|
1576
1599
|
authorizationRequestPayload,
|
|
1577
|
-
|
|
1600
|
+
// If client_id_scheme was provided we should use the legacy (unprefixed) client id scheme
|
|
1601
|
+
// TODO: allow both versions, in case of e.g. did:
|
|
1602
|
+
expectedClientId: expectedClientId.legacyClientId ?? expectedClientId.clientId
|
|
1578
1603
|
});
|
|
1579
1604
|
}
|
|
1580
1605
|
const authorizationResponsePayload = parseOpenid4VpAuthorizationResponsePayload(authorizationResponse);
|