@openid4vc/openid4vp 0.3.0-alpha-20250225204254 → 0.3.0-alpha-20250227094616
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 +74 -27
- package/dist/index.d.ts +74 -27
- package/dist/index.js +432 -265
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +366 -199
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -345,9 +345,19 @@ var zOpenid4vpAuthorizationRequest = z7.object({
|
|
|
345
345
|
presentation_definition_uri: zHttpsUrl3.optional(),
|
|
346
346
|
dcql_query: z7.record(z7.any()).optional(),
|
|
347
347
|
client_metadata: zClientMetadata.optional(),
|
|
348
|
+
client_metadata_uri: zHttpsUrl3.optional(),
|
|
348
349
|
state: z7.string().optional(),
|
|
349
350
|
transaction_data: z7.array(z7.string()).optional(),
|
|
350
|
-
trust_chain: z7.unknown().optional()
|
|
351
|
+
trust_chain: z7.unknown().optional(),
|
|
352
|
+
client_id_scheme: z7.enum([
|
|
353
|
+
"pre-registered",
|
|
354
|
+
"redirect_uri",
|
|
355
|
+
"entity_id",
|
|
356
|
+
"did",
|
|
357
|
+
"verifier_attestation",
|
|
358
|
+
"x509_san_dns",
|
|
359
|
+
"x509_san_uri"
|
|
360
|
+
]).optional()
|
|
351
361
|
}).passthrough();
|
|
352
362
|
|
|
353
363
|
// src/authorization-request/z-authorization-request-dc-api.ts
|
|
@@ -364,21 +374,30 @@ var zOpenid4vpAuthorizationRequestDcApi = zOpenid4vpAuthorizationRequest.pick({
|
|
|
364
374
|
}).extend({
|
|
365
375
|
client_id: z8.optional(z8.string()),
|
|
366
376
|
expected_origins: z8.array(z8.string()).optional(),
|
|
367
|
-
response_mode: z8.enum(["dc_api", "dc_api.jwt"])
|
|
377
|
+
response_mode: z8.enum(["dc_api", "dc_api.jwt", "w3c_dc_api.jwt", "w3c_dc_api"]),
|
|
378
|
+
client_id_scheme: z8.enum([
|
|
379
|
+
"pre-registered",
|
|
380
|
+
"redirect_uri",
|
|
381
|
+
"entity_id",
|
|
382
|
+
"did",
|
|
383
|
+
"verifier_attestation",
|
|
384
|
+
"x509_san_dns",
|
|
385
|
+
"x509_san_uri"
|
|
386
|
+
]).optional()
|
|
368
387
|
}).strip();
|
|
369
388
|
function isOpenid4vpAuthorizationRequestDcApi(request) {
|
|
370
|
-
return request.response_mode === "dc_api" || request.response_mode === "dc_api.jwt";
|
|
389
|
+
return request.response_mode === "dc_api" || request.response_mode === "dc_api.jwt" || request.response_mode === "w3c_dc_api.jwt" || request.response_mode === "w3c_dc_api";
|
|
371
390
|
}
|
|
372
391
|
|
|
373
392
|
// src/authorization-request/create-authorization-request.ts
|
|
374
393
|
async function createOpenid4vpAuthorizationRequest(options) {
|
|
375
|
-
const { jar, scheme = "openid4vp://",
|
|
394
|
+
const { jar, scheme = "openid4vp://", requestPayload, wallet, callbacks } = options;
|
|
376
395
|
let additionalJwtPayload;
|
|
377
396
|
let authRequestParams;
|
|
378
|
-
if (isOpenid4vpAuthorizationRequestDcApi(
|
|
397
|
+
if (isOpenid4vpAuthorizationRequestDcApi(requestPayload)) {
|
|
379
398
|
authRequestParams = parseWithErrorHandling2(
|
|
380
399
|
zOpenid4vpAuthorizationRequestDcApi,
|
|
381
|
-
|
|
400
|
+
requestPayload,
|
|
382
401
|
"Invalid authorization request. Could not parse openid4vp dc_api authorization request."
|
|
383
402
|
);
|
|
384
403
|
if (jar && !authRequestParams.expected_origins) {
|
|
@@ -394,7 +413,7 @@ async function createOpenid4vpAuthorizationRequest(options) {
|
|
|
394
413
|
} else {
|
|
395
414
|
authRequestParams = parseWithErrorHandling2(
|
|
396
415
|
zOpenid4vpAuthorizationRequest,
|
|
397
|
-
|
|
416
|
+
requestPayload,
|
|
398
417
|
"Invalid authorization request. Could not parse openid4vp authorization request."
|
|
399
418
|
);
|
|
400
419
|
validateOpenid4vpAuthorizationRequestPayload({ params: authRequestParams, walletVerificationOptions: wallet });
|
|
@@ -407,7 +426,7 @@ async function createOpenid4vpAuthorizationRequest(options) {
|
|
|
407
426
|
if (jar) {
|
|
408
427
|
const jarResult = await createJarAuthRequest({
|
|
409
428
|
...jar,
|
|
410
|
-
authRequestParams:
|
|
429
|
+
authRequestParams: requestPayload,
|
|
411
430
|
additionalJwtPayload,
|
|
412
431
|
callbacks
|
|
413
432
|
});
|
|
@@ -425,10 +444,10 @@ async function createOpenid4vpAuthorizationRequest(options) {
|
|
|
425
444
|
const url = new URL(scheme);
|
|
426
445
|
url.search = `?${new URLSearchParams([
|
|
427
446
|
...url.searchParams.entries(),
|
|
428
|
-
...objectToQueryParams(
|
|
447
|
+
...objectToQueryParams(requestPayload).entries()
|
|
429
448
|
]).toString()}`;
|
|
430
449
|
return {
|
|
431
|
-
authRequestObject:
|
|
450
|
+
authRequestObject: requestPayload,
|
|
432
451
|
authRequest: url.toString(),
|
|
433
452
|
jar: void 0
|
|
434
453
|
};
|
|
@@ -514,17 +533,82 @@ function parseOpenid4vpAuthorizationRequestPayload(options) {
|
|
|
514
533
|
}
|
|
515
534
|
|
|
516
535
|
// src/authorization-request/resolve-authorization-request.ts
|
|
517
|
-
import { Oauth2ErrorCodes as
|
|
536
|
+
import { Oauth2ErrorCodes as Oauth2ErrorCodes9, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError10 } from "@openid4vc/oauth2";
|
|
518
537
|
import { parseWithErrorHandling as parseWithErrorHandling4 } from "@openid4vc/utils";
|
|
519
|
-
import
|
|
538
|
+
import z15 from "zod";
|
|
539
|
+
|
|
540
|
+
// src/client-identifier-scheme/parse-client-identifier-scheme.ts
|
|
541
|
+
import { Oauth2ErrorCodes as Oauth2ErrorCodes4, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError5, getGlobalConfig } from "@openid4vc/oauth2";
|
|
542
|
+
|
|
543
|
+
// src/version.ts
|
|
544
|
+
import { Oauth2ErrorCodes as Oauth2ErrorCodes3, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError4 } from "@openid4vc/oauth2";
|
|
545
|
+
function parseAuthorizationRequestVersion(request) {
|
|
546
|
+
const requirements = [];
|
|
547
|
+
const vp_formats = request.client_metadata?.vp_formats;
|
|
548
|
+
if (isOpenid4vpAuthorizationRequestDcApi(request) && (request.response_mode === "w3c_dc_api" || request.response_mode === "w3c_dc_api.jwt")) {
|
|
549
|
+
requirements.push(["<", 23]);
|
|
550
|
+
requirements.push([">=", 21]);
|
|
551
|
+
}
|
|
552
|
+
if (isOpenid4vpAuthorizationRequestDcApi(request) && request.response_mode === "dc_api" || request.response_mode === "dc_api.jwt") {
|
|
553
|
+
requirements.push([">=", 23]);
|
|
554
|
+
}
|
|
555
|
+
if (isOpenid4vpAuthorizationRequestDcApi(request) && (request.transaction_data || request.dcql_query)) {
|
|
556
|
+
requirements.push([">=", 23]);
|
|
557
|
+
}
|
|
558
|
+
if (request.dcql_query) {
|
|
559
|
+
requirements.push([">=", 22]);
|
|
560
|
+
}
|
|
561
|
+
if (request.transaction_data) {
|
|
562
|
+
requirements.push([">=", 22]);
|
|
563
|
+
}
|
|
564
|
+
if (request.client_id_scheme) {
|
|
565
|
+
requirements.push(["<", 22]);
|
|
566
|
+
}
|
|
567
|
+
if (request.client_id) {
|
|
568
|
+
const colonIndex = request.client_id.indexOf(":");
|
|
569
|
+
const schemePart = request.client_id.substring(0, colonIndex);
|
|
570
|
+
const parsedScheme = zClientIdScheme.safeParse(schemePart);
|
|
571
|
+
if (parsedScheme.success && parsedScheme.data !== "did" && parsedScheme.data !== "https") {
|
|
572
|
+
requirements.push([">=", 22]);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (!request.client_id) {
|
|
576
|
+
requirements.push([">=", 21]);
|
|
577
|
+
}
|
|
578
|
+
if ("client_metadata_uri" in request) {
|
|
579
|
+
requirements.push(["<", 21]);
|
|
580
|
+
}
|
|
581
|
+
if (isOpenid4vpAuthorizationRequestDcApi(request)) {
|
|
582
|
+
requirements.push([">=", 21]);
|
|
583
|
+
}
|
|
584
|
+
if ("request_uri_method" in request || "wallet_nonce" in request) {
|
|
585
|
+
requirements.push([">=", 21]);
|
|
586
|
+
}
|
|
587
|
+
if (request.client_id_scheme === "verifier_attestation") {
|
|
588
|
+
requirements.push([">=", 20]);
|
|
589
|
+
}
|
|
590
|
+
if (request.client_id_scheme === "x509_san_dns" || request.client_id_scheme === "x509_san_uri") {
|
|
591
|
+
requirements.push([">=", 19]);
|
|
592
|
+
}
|
|
593
|
+
const lessThanVersions = requirements.filter(([operator]) => operator === "<").map(([_, version]) => version);
|
|
594
|
+
const greaterThanVersions = requirements.filter(([operator]) => operator === ">=").map(([_, version]) => version);
|
|
595
|
+
const highestPossibleVersion = lessThanVersions.length > 0 ? Math.max(Math.min(...lessThanVersions) - 1, 18) : 24;
|
|
596
|
+
const lowestRequiredVersion = greaterThanVersions.length > 0 ? Math.max(...greaterThanVersions) : 18;
|
|
597
|
+
if (lowestRequiredVersion > highestPossibleVersion) {
|
|
598
|
+
throw new Oauth2ServerErrorResponseError4({
|
|
599
|
+
error: Oauth2ErrorCodes3.InvalidRequest,
|
|
600
|
+
error_description: "Could not infer openid4vp version from the openid4vp request payload."
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
return highestPossibleVersion;
|
|
604
|
+
}
|
|
520
605
|
|
|
521
606
|
// src/client-identifier-scheme/parse-client-identifier-scheme.ts
|
|
522
|
-
import { Oauth2ErrorCodes as Oauth2ErrorCodes3, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError4, getGlobalConfig } from "@openid4vc/oauth2";
|
|
523
607
|
function getClientId(options) {
|
|
524
608
|
if (isOpenid4vpAuthorizationRequestDcApi(options.request)) {
|
|
525
609
|
if (!options.origin) {
|
|
526
|
-
throw new
|
|
527
|
-
error:
|
|
610
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
611
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
528
612
|
error_description: "Failed to parse client identifier. 'origin' is required for requests with response_mode 'dc_api' and 'dc_api.jwt'"
|
|
529
613
|
});
|
|
530
614
|
}
|
|
@@ -533,10 +617,48 @@ function getClientId(options) {
|
|
|
533
617
|
}
|
|
534
618
|
return options.request.client_id;
|
|
535
619
|
}
|
|
620
|
+
function getLegacyClientId(options) {
|
|
621
|
+
const legacyClientIdScheme = options.request.client_id_scheme ?? "pre-registered";
|
|
622
|
+
let clientIdScheme;
|
|
623
|
+
if (legacyClientIdScheme === "entity_id") {
|
|
624
|
+
clientIdScheme = "https";
|
|
625
|
+
} else {
|
|
626
|
+
clientIdScheme = legacyClientIdScheme;
|
|
627
|
+
}
|
|
628
|
+
if (isOpenid4vpAuthorizationRequestDcApi(options.request)) {
|
|
629
|
+
if (!options.origin) {
|
|
630
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
631
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
632
|
+
error_description: "Failed to parse client identifier. 'origin' is required for requests with response_mode 'dc_api' and 'dc_api.jwt'"
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
if (!options.jar || !options.request.client_id) return `web-origin:${options.origin}`;
|
|
636
|
+
return `${clientIdScheme}:${options.request.client_id}`;
|
|
637
|
+
}
|
|
638
|
+
if (clientIdScheme === "https" || clientIdScheme === "did") {
|
|
639
|
+
return options.request.client_id;
|
|
640
|
+
}
|
|
641
|
+
if (clientIdScheme === "pre-registered") {
|
|
642
|
+
return options.request.client_id;
|
|
643
|
+
}
|
|
644
|
+
return `${clientIdScheme}:${options.request.client_id}`;
|
|
645
|
+
}
|
|
536
646
|
function parseClientIdentifier(options, parserConfig) {
|
|
537
647
|
const { request, jar } = options;
|
|
648
|
+
const version = parseAuthorizationRequestVersion(request);
|
|
649
|
+
if (version < 22) {
|
|
650
|
+
const legacyClientIdScheme = request.client_id_scheme ?? "pre-registered";
|
|
651
|
+
let clientIdSchem;
|
|
652
|
+
if (legacyClientIdScheme) {
|
|
653
|
+
if (legacyClientIdScheme === "entity_id") {
|
|
654
|
+
clientIdSchem = "https";
|
|
655
|
+
} else {
|
|
656
|
+
clientIdSchem = legacyClientIdScheme;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
538
660
|
const isDcApiRequest = isOpenid4vpAuthorizationRequestDcApi(request);
|
|
539
|
-
const clientId = getClientId(options);
|
|
661
|
+
const clientId = version < 22 ? getLegacyClientId(options) : getClientId(options);
|
|
540
662
|
const parserConfigWithDefaults = {
|
|
541
663
|
supportedSchemes: parserConfig?.supportedSchemes || Object.values(zClientIdScheme.options)
|
|
542
664
|
};
|
|
@@ -552,22 +674,22 @@ function parseClientIdentifier(options, parserConfig) {
|
|
|
552
674
|
const schemePart = clientId.substring(0, colonIndex);
|
|
553
675
|
const identifierPart = clientId.substring(colonIndex + 1);
|
|
554
676
|
if (!parserConfigWithDefaults.supportedSchemes.includes(schemePart)) {
|
|
555
|
-
throw new
|
|
556
|
-
error:
|
|
677
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
678
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
557
679
|
error_description: `Unsupported client identifier scheme. ${schemePart} is not supported.`
|
|
558
680
|
});
|
|
559
681
|
}
|
|
560
682
|
const scheme = schemePart;
|
|
561
683
|
if (scheme === "https") {
|
|
562
684
|
if (isDcApiRequest) {
|
|
563
|
-
throw new
|
|
564
|
-
error:
|
|
685
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
686
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
565
687
|
error_description: `The client identifier scheme 'https' is not supported when using the dc_api response mode.`
|
|
566
688
|
});
|
|
567
689
|
}
|
|
568
690
|
if (!clientId.startsWith("https://") && !(getGlobalConfig().allowInsecureUrls && clientId.startsWith("http://"))) {
|
|
569
|
-
throw new
|
|
570
|
-
error:
|
|
691
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
692
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
571
693
|
error_description: "Invalid client identifier. Client identifier must start with https:// or http:// if allowInsecureUrls is true."
|
|
572
694
|
});
|
|
573
695
|
}
|
|
@@ -580,14 +702,14 @@ function parseClientIdentifier(options, parserConfig) {
|
|
|
580
702
|
}
|
|
581
703
|
if (scheme === "redirect_uri") {
|
|
582
704
|
if (jar) {
|
|
583
|
-
throw new
|
|
584
|
-
error:
|
|
705
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
706
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
585
707
|
error_description: 'Using client identifier scheme "redirect_uri" the request MUST NOT be signed.'
|
|
586
708
|
});
|
|
587
709
|
}
|
|
588
710
|
if (isOpenid4vpAuthorizationRequestDcApi(request)) {
|
|
589
|
-
throw new
|
|
590
|
-
error:
|
|
711
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
712
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
591
713
|
error_description: `The client identifier scheme 'redirect_uri' is not supported when using the dc_api response mode.`
|
|
592
714
|
});
|
|
593
715
|
}
|
|
@@ -600,26 +722,26 @@ function parseClientIdentifier(options, parserConfig) {
|
|
|
600
722
|
}
|
|
601
723
|
if (scheme === "did") {
|
|
602
724
|
if (!jar) {
|
|
603
|
-
throw new
|
|
604
|
-
error:
|
|
725
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
726
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
605
727
|
error_description: 'Using client identifier scheme "did" requires a signed JAR request.'
|
|
606
728
|
});
|
|
607
729
|
}
|
|
608
730
|
if (!clientId.startsWith("did:")) {
|
|
609
|
-
throw new
|
|
610
|
-
error:
|
|
731
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
732
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
611
733
|
error_description: "Invalid client identifier. Client identifier must start with 'did:'"
|
|
612
734
|
});
|
|
613
735
|
}
|
|
614
736
|
if (!jar.signer.publicJwk.kid) {
|
|
615
|
-
throw new
|
|
616
|
-
error:
|
|
737
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
738
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
617
739
|
error_description: `Missing required 'kid' for client identifier scheme: did`
|
|
618
740
|
});
|
|
619
741
|
}
|
|
620
742
|
if (!jar.signer.publicJwk.kid?.startsWith(clientId)) {
|
|
621
|
-
throw new
|
|
622
|
-
error:
|
|
743
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
744
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
623
745
|
error_description: 'With client identifier scheme "did" the JAR request must be signed by the same DID as the client identifier.'
|
|
624
746
|
});
|
|
625
747
|
}
|
|
@@ -632,22 +754,22 @@ function parseClientIdentifier(options, parserConfig) {
|
|
|
632
754
|
}
|
|
633
755
|
if (scheme === "x509_san_dns" || scheme === "x509_san_uri") {
|
|
634
756
|
if (!jar) {
|
|
635
|
-
throw new
|
|
636
|
-
error:
|
|
757
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
758
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
637
759
|
error_description: 'Using client identifier scheme "x509_san_dns" or "x509_san_uri" requires a signed JAR request.'
|
|
638
760
|
});
|
|
639
761
|
}
|
|
640
762
|
if (jar.signer.method !== "x5c") {
|
|
641
|
-
throw new
|
|
642
|
-
error:
|
|
763
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
764
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
643
765
|
error_description: "Something went wrong. The JWT signer method is not x5c but the client identifier scheme is x509_san_dns."
|
|
644
766
|
});
|
|
645
767
|
}
|
|
646
768
|
if (scheme === "x509_san_dns") {
|
|
647
769
|
if (!options.callbacks.getX509CertificateMetadata) {
|
|
648
|
-
throw new
|
|
770
|
+
throw new Oauth2ServerErrorResponseError5(
|
|
649
771
|
{
|
|
650
|
-
error:
|
|
772
|
+
error: Oauth2ErrorCodes4.ServerError
|
|
651
773
|
},
|
|
652
774
|
{
|
|
653
775
|
internalMessage: "Missing required 'getX509CertificateMetadata' callback for verification of 'x509_san_dns' client id scheme"
|
|
@@ -656,25 +778,25 @@ function parseClientIdentifier(options, parserConfig) {
|
|
|
656
778
|
}
|
|
657
779
|
const { sanDnsNames } = options.callbacks.getX509CertificateMetadata(jar.signer.x5c[0]);
|
|
658
780
|
if (!sanDnsNames.includes(identifierPart)) {
|
|
659
|
-
throw new
|
|
660
|
-
error:
|
|
661
|
-
error_description:
|
|
781
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
782
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
783
|
+
error_description: `Invalid client identifier. One of the leaf certificates san dns names [${sanDnsNames.join(", ")}] must match the client identifier '${identifierPart}'. `
|
|
662
784
|
});
|
|
663
785
|
}
|
|
664
786
|
if (!isOpenid4vpAuthorizationRequestDcApi(request)) {
|
|
665
787
|
const uri = request.redirect_uri ?? request.response_uri;
|
|
666
788
|
if (!uri || getDomainFromUrl(uri) !== identifierPart) {
|
|
667
|
-
throw new
|
|
668
|
-
error:
|
|
789
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
790
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
669
791
|
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."
|
|
670
792
|
});
|
|
671
793
|
}
|
|
672
794
|
}
|
|
673
795
|
} else if (scheme === "x509_san_uri") {
|
|
674
796
|
if (!options.callbacks.getX509CertificateMetadata) {
|
|
675
|
-
throw new
|
|
797
|
+
throw new Oauth2ServerErrorResponseError5(
|
|
676
798
|
{
|
|
677
|
-
error:
|
|
799
|
+
error: Oauth2ErrorCodes4.ServerError
|
|
678
800
|
},
|
|
679
801
|
{
|
|
680
802
|
internalMessage: "Missing required 'getX509CertificateMetadata' callback for verification of 'x509_san_uri' client id scheme"
|
|
@@ -683,16 +805,16 @@ function parseClientIdentifier(options, parserConfig) {
|
|
|
683
805
|
}
|
|
684
806
|
const { sanUriNames } = options.callbacks.getX509CertificateMetadata(jar.signer.x5c[0]);
|
|
685
807
|
if (!sanUriNames.includes(identifierPart)) {
|
|
686
|
-
throw new
|
|
687
|
-
error:
|
|
688
|
-
error_description:
|
|
808
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
809
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
810
|
+
error_description: `Invalid client identifier. One of the leaf certificates san uri names [${sanUriNames.join(", ")}] must match the client identifier '${identifierPart}'.`
|
|
689
811
|
});
|
|
690
812
|
}
|
|
691
813
|
if (!isOpenid4vpAuthorizationRequestDcApi(request)) {
|
|
692
814
|
const uri = request.redirect_uri || request.response_uri;
|
|
693
815
|
if (!uri || uri !== identifierPart) {
|
|
694
|
-
throw new
|
|
695
|
-
error:
|
|
816
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
817
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
696
818
|
error_description: "The redirect_uri value MUST match the Client Identifier without the prefix x509_san_uri"
|
|
697
819
|
});
|
|
698
820
|
}
|
|
@@ -715,8 +837,8 @@ function parseClientIdentifier(options, parserConfig) {
|
|
|
715
837
|
}
|
|
716
838
|
if (scheme === "verifier_attestation") {
|
|
717
839
|
if (!jar) {
|
|
718
|
-
throw new
|
|
719
|
-
error:
|
|
840
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
841
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
720
842
|
error_description: 'Using client identifier scheme "verifier_attestation" requires a signed JAR request.'
|
|
721
843
|
});
|
|
722
844
|
}
|
|
@@ -733,17 +855,57 @@ function getDomainFromUrl(url) {
|
|
|
733
855
|
const domain = url.split("://")[1].split(regex)[0];
|
|
734
856
|
return domain;
|
|
735
857
|
} catch (error) {
|
|
736
|
-
throw new
|
|
737
|
-
error:
|
|
858
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
859
|
+
error: Oauth2ErrorCodes4.ServerError,
|
|
738
860
|
error_description: `Url '${url}' is not a valid URL`
|
|
739
861
|
});
|
|
740
862
|
}
|
|
741
863
|
}
|
|
742
864
|
|
|
865
|
+
// src/fetch-client-metadata.ts
|
|
866
|
+
import { Oauth2ErrorCodes as Oauth2ErrorCodes5, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError6 } from "@openid4vc/oauth2";
|
|
867
|
+
import { ContentType, createZodFetcher } from "@openid4vc/utils";
|
|
868
|
+
|
|
869
|
+
// src/models/z-wallet-metadata.ts
|
|
870
|
+
import { z as z11 } from "zod";
|
|
871
|
+
var zWalletMetadata = z11.object({
|
|
872
|
+
presentation_definition_uri_supported: z11.optional(z11.boolean()),
|
|
873
|
+
vp_formats_supported: zVpFormatsSupported,
|
|
874
|
+
client_id_schemes_supported: z11.optional(z11.array(zClientIdScheme)),
|
|
875
|
+
request_object_signing_alg_values_supported: z11.optional(z11.array(z11.string())),
|
|
876
|
+
authorization_encryption_alg_values_supported: z11.optional(z11.array(z11.string())),
|
|
877
|
+
authorization_encryption_enc_values_supported: z11.optional(z11.array(z11.string()))
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
// src/fetch-client-metadata.ts
|
|
881
|
+
async function fetchClientMetadata(options) {
|
|
882
|
+
const { fetch, clientMetadataUri } = options;
|
|
883
|
+
const fetcher = createZodFetcher(fetch);
|
|
884
|
+
const { result, response } = await fetcher(zWalletMetadata, ContentType.Json, clientMetadataUri, {
|
|
885
|
+
method: "GET",
|
|
886
|
+
headers: {
|
|
887
|
+
Accept: ContentType.Json
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
if (!response.ok) {
|
|
891
|
+
throw new Oauth2ServerErrorResponseError6({
|
|
892
|
+
error_description: `Fetching client metadata from '${clientMetadataUri}' failed with status code '${response.status}'.`,
|
|
893
|
+
error: Oauth2ErrorCodes5.InvalidRequestUri
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
if (!result || !result.success) {
|
|
897
|
+
throw new Oauth2ServerErrorResponseError6({
|
|
898
|
+
error_description: `Parsing client metadata from '${clientMetadataUri}' failed.`,
|
|
899
|
+
error: Oauth2ErrorCodes5.InvalidRequestObject
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
return result.data;
|
|
903
|
+
}
|
|
904
|
+
|
|
743
905
|
// src/jar/handle-jar-request/verify-jar-request.ts
|
|
744
906
|
import {
|
|
745
|
-
Oauth2ErrorCodes as
|
|
746
|
-
Oauth2ServerErrorResponseError as
|
|
907
|
+
Oauth2ErrorCodes as Oauth2ErrorCodes7,
|
|
908
|
+
Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError8,
|
|
747
909
|
decodeJwt as decodeJwt3,
|
|
748
910
|
jwtSignerFromJwt as jwtSignerFromJwt2,
|
|
749
911
|
verifyJwt,
|
|
@@ -752,35 +914,35 @@ import {
|
|
|
752
914
|
} from "@openid4vc/oauth2";
|
|
753
915
|
|
|
754
916
|
// src/jar/jar-request-object/fetch-jar-request-object.ts
|
|
755
|
-
import { Oauth2ErrorCodes as
|
|
756
|
-
import { ContentType, createZodFetcher, objectToQueryParams as objectToQueryParams2 } from "@openid4vc/utils";
|
|
757
|
-
import { z as
|
|
917
|
+
import { Oauth2ErrorCodes as Oauth2ErrorCodes6, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError7 } from "@openid4vc/oauth2";
|
|
918
|
+
import { ContentType as ContentType2, createZodFetcher as createZodFetcher2, objectToQueryParams as objectToQueryParams2 } from "@openid4vc/utils";
|
|
919
|
+
import { z as z12 } from "zod";
|
|
758
920
|
async function fetchJarRequestObject(options) {
|
|
759
921
|
const { requestUri, clientIdentifierScheme, method, wallet, fetch } = options;
|
|
760
|
-
const fetcher =
|
|
922
|
+
const fetcher = createZodFetcher2(fetch);
|
|
761
923
|
let requestBody = wallet.metadata ? { wallet_metadata: wallet.metadata, wallet_nonce: wallet.nonce } : void 0;
|
|
762
924
|
if (requestBody?.wallet_metadata?.request_object_signing_alg_values_supported && clientIdentifierScheme === "redirect_uri") {
|
|
763
925
|
const { request_object_signing_alg_values_supported, ...rest } = requestBody.wallet_metadata;
|
|
764
926
|
requestBody = { ...requestBody, wallet_metadata: { ...rest } };
|
|
765
927
|
}
|
|
766
|
-
const { result, response } = await fetcher(
|
|
928
|
+
const { result, response } = await fetcher(z12.string(), ContentType2.OAuthRequestObjectJwt, requestUri, {
|
|
767
929
|
method,
|
|
768
930
|
headers: {
|
|
769
|
-
Accept: `${
|
|
770
|
-
"Content-Type":
|
|
931
|
+
Accept: `${ContentType2.OAuthRequestObjectJwt}, ${ContentType2.Jwt};q=0.9`,
|
|
932
|
+
"Content-Type": ContentType2.XWwwFormUrlencoded
|
|
771
933
|
},
|
|
772
934
|
body: method === "POST" ? objectToQueryParams2(wallet.metadata ?? {}) : void 0
|
|
773
935
|
});
|
|
774
936
|
if (!response.ok) {
|
|
775
|
-
throw new
|
|
937
|
+
throw new Oauth2ServerErrorResponseError7({
|
|
776
938
|
error_description: `Fetching request_object from request_uri '${requestUri}' failed with status code '${response.status}'.`,
|
|
777
|
-
error:
|
|
939
|
+
error: Oauth2ErrorCodes6.InvalidRequestUri
|
|
778
940
|
});
|
|
779
941
|
}
|
|
780
942
|
if (!result || !result.success) {
|
|
781
|
-
throw new
|
|
943
|
+
throw new Oauth2ServerErrorResponseError7({
|
|
782
944
|
error_description: `Parsing request_object from request_uri '${requestUri}' failed.`,
|
|
783
|
-
error:
|
|
945
|
+
error: Oauth2ErrorCodes6.InvalidRequestObject
|
|
784
946
|
});
|
|
785
947
|
}
|
|
786
948
|
return result.data;
|
|
@@ -788,10 +950,10 @@ async function fetchJarRequestObject(options) {
|
|
|
788
950
|
|
|
789
951
|
// src/jar/jar-request-object/z-jar-request-object.ts
|
|
790
952
|
import { zJwtPayload as zJwtPayload2 } from "@openid4vc/oauth2";
|
|
791
|
-
import { z as
|
|
792
|
-
var zJarRequestObjectPayload =
|
|
953
|
+
import { z as z13 } from "zod";
|
|
954
|
+
var zJarRequestObjectPayload = z13.object({
|
|
793
955
|
...zJwtPayload2.shape,
|
|
794
|
-
client_id:
|
|
956
|
+
client_id: z13.string()
|
|
795
957
|
}).passthrough();
|
|
796
958
|
|
|
797
959
|
// src/jar/handle-jar-request/verify-jar-request.ts
|
|
@@ -802,8 +964,8 @@ async function verifyJarRequest(options) {
|
|
|
802
964
|
const clientIdentifierScheme = jarRequestParams.client_id ? zClientIdScheme.parse(jarRequestParams.client_id.split(":")[0]) : "web-origin";
|
|
803
965
|
const method = jarRequestParams.request_uri_method ?? "GET";
|
|
804
966
|
if (method !== "GET" && method !== "POST") {
|
|
805
|
-
throw new
|
|
806
|
-
error:
|
|
967
|
+
throw new Oauth2ServerErrorResponseError8({
|
|
968
|
+
error: Oauth2ErrorCodes7.InvalidRequestUriMethod,
|
|
807
969
|
error_description: "Invalid request_uri_method. Must be GET or POST."
|
|
808
970
|
});
|
|
809
971
|
}
|
|
@@ -817,8 +979,8 @@ async function verifyJarRequest(options) {
|
|
|
817
979
|
const { decryptionJwk, payload: decryptedRequestObject } = requestObjectIsEncrypted ? await decryptJarRequest({ jwe: requestObject, callbacks }) : { payload: requestObject, decryptionJwk: void 0 };
|
|
818
980
|
const requestIsSigned = zCompactJwt2.safeParse(decryptedRequestObject).success;
|
|
819
981
|
if (!requestIsSigned) {
|
|
820
|
-
throw new
|
|
821
|
-
error:
|
|
982
|
+
throw new Oauth2ServerErrorResponseError8({
|
|
983
|
+
error: Oauth2ErrorCodes7.InvalidRequestObject,
|
|
822
984
|
error_description: "Jar Request Object is not a valid JWS."
|
|
823
985
|
});
|
|
824
986
|
}
|
|
@@ -827,14 +989,14 @@ async function verifyJarRequest(options) {
|
|
|
827
989
|
callbacks
|
|
828
990
|
});
|
|
829
991
|
if (!authRequestParams.client_id) {
|
|
830
|
-
throw new
|
|
831
|
-
error:
|
|
992
|
+
throw new Oauth2ServerErrorResponseError8({
|
|
993
|
+
error: Oauth2ErrorCodes7.InvalidRequestObject,
|
|
832
994
|
error_description: 'Jar Request Object is missing the required "client_id" field.'
|
|
833
995
|
});
|
|
834
996
|
}
|
|
835
997
|
if (jarRequestParams.client_id !== authRequestParams.client_id) {
|
|
836
|
-
throw new
|
|
837
|
-
error:
|
|
998
|
+
throw new Oauth2ServerErrorResponseError8({
|
|
999
|
+
error: Oauth2ErrorCodes7.InvalidRequest,
|
|
838
1000
|
error_description: "client_id does not match the request object client_id."
|
|
839
1001
|
});
|
|
840
1002
|
}
|
|
@@ -849,14 +1011,14 @@ async function decryptJarRequest(options) {
|
|
|
849
1011
|
const { jwe, callbacks } = options;
|
|
850
1012
|
const { header } = decodeJwt3({ jwt: jwe });
|
|
851
1013
|
if (!header.kid) {
|
|
852
|
-
throw new
|
|
853
|
-
error:
|
|
1014
|
+
throw new Oauth2ServerErrorResponseError8({
|
|
1015
|
+
error: Oauth2ErrorCodes7.InvalidRequestObject,
|
|
854
1016
|
error_description: 'Jar JWE is missing the protected header field "kid".'
|
|
855
1017
|
});
|
|
856
1018
|
}
|
|
857
1019
|
const decryptionResult = await callbacks.decryptJwe(jwe);
|
|
858
1020
|
if (!decryptionResult.decrypted) {
|
|
859
|
-
throw new
|
|
1021
|
+
throw new Oauth2ServerErrorResponseError8({
|
|
860
1022
|
error: "invalid_request_object",
|
|
861
1023
|
error_description: "Failed to decrypt jar request object."
|
|
862
1024
|
});
|
|
@@ -874,21 +1036,28 @@ async function verifyJarRequestObject(options) {
|
|
|
874
1036
|
payload: jwt.payload,
|
|
875
1037
|
signer: jwtSigner
|
|
876
1038
|
});
|
|
1039
|
+
const version = parseAuthorizationRequestVersion(jwt.payload);
|
|
1040
|
+
if (jwt.header.typ !== "oauth-authz-req+jwt" && version >= 24) {
|
|
1041
|
+
throw new Oauth2ServerErrorResponseError8({
|
|
1042
|
+
error: Oauth2ErrorCodes7.InvalidRequestObject,
|
|
1043
|
+
error_description: `Invalid Jar Request Object typ header. Expected "oauth-authz-req+jwt", received "${jwt.header.typ}".`
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
877
1046
|
return { authRequestParams: jwt.payload, signer };
|
|
878
1047
|
}
|
|
879
1048
|
|
|
880
1049
|
// src/transaction-data/parse-transaction-data.ts
|
|
881
|
-
import { Oauth2ErrorCodes as
|
|
1050
|
+
import { Oauth2ErrorCodes as Oauth2ErrorCodes8, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError9 } from "@openid4vc/oauth2";
|
|
882
1051
|
import { decodeBase64, encodeToUtf8String, parseIfJson } from "@openid4vc/utils";
|
|
883
1052
|
|
|
884
1053
|
// src/transaction-data/z-transaction-data.ts
|
|
885
|
-
import { z as
|
|
886
|
-
var zTransactionEntry =
|
|
887
|
-
type:
|
|
888
|
-
credential_ids:
|
|
889
|
-
transaction_data_hashes_alg:
|
|
1054
|
+
import { z as z14 } from "zod";
|
|
1055
|
+
var zTransactionEntry = z14.object({
|
|
1056
|
+
type: z14.string(),
|
|
1057
|
+
credential_ids: z14.array(z14.string()).min(1),
|
|
1058
|
+
transaction_data_hashes_alg: z14.array(z14.string()).optional()
|
|
890
1059
|
});
|
|
891
|
-
var zTransactionData =
|
|
1060
|
+
var zTransactionData = z14.array(zTransactionEntry);
|
|
892
1061
|
|
|
893
1062
|
// src/transaction-data/parse-transaction-data.ts
|
|
894
1063
|
function parseTransactionData(options) {
|
|
@@ -896,8 +1065,8 @@ function parseTransactionData(options) {
|
|
|
896
1065
|
const decoded = transactionData.map((tdEntry) => parseIfJson(encodeToUtf8String(decodeBase64(tdEntry))));
|
|
897
1066
|
const parsedResult = zTransactionData.safeParse(decoded);
|
|
898
1067
|
if (!parsedResult.success) {
|
|
899
|
-
throw new
|
|
900
|
-
error:
|
|
1068
|
+
throw new Oauth2ServerErrorResponseError9({
|
|
1069
|
+
error: Oauth2ErrorCodes8.InvalidTransactionData,
|
|
901
1070
|
error_description: "Failed to parse transaction data."
|
|
902
1071
|
});
|
|
903
1072
|
}
|
|
@@ -906,18 +1075,18 @@ function parseTransactionData(options) {
|
|
|
906
1075
|
|
|
907
1076
|
// src/authorization-request/resolve-authorization-request.ts
|
|
908
1077
|
async function resolveOpenid4vpAuthorizationRequest(options) {
|
|
909
|
-
const {
|
|
1078
|
+
const { requestPayload, wallet, callbacks, origin, omitOriginValidation } = options;
|
|
910
1079
|
let authRequestPayload;
|
|
911
1080
|
const parsed = parseWithErrorHandling4(
|
|
912
|
-
|
|
913
|
-
|
|
1081
|
+
z15.union([zOpenid4vpAuthorizationRequestDcApi, zOpenid4vpAuthorizationRequest, zJarAuthRequest]),
|
|
1082
|
+
requestPayload,
|
|
914
1083
|
"Invalid authorization request. Could not parse openid4vp authorization request as openid4vp or jar auth request."
|
|
915
1084
|
);
|
|
916
1085
|
let jar;
|
|
917
1086
|
if (isJarAuthRequest(parsed)) {
|
|
918
1087
|
jar = await verifyJarRequest({ jarRequestParams: parsed, callbacks, wallet });
|
|
919
1088
|
const parsedJarAuthRequestPayload = parseWithErrorHandling4(
|
|
920
|
-
|
|
1089
|
+
z15.union([zOpenid4vpAuthorizationRequestDcApi, zOpenid4vpAuthorizationRequest]),
|
|
921
1090
|
jar.authRequestParams,
|
|
922
1091
|
"Invalid authorization request. Could not parse jar request payload as openid4vp auth request."
|
|
923
1092
|
);
|
|
@@ -937,13 +1106,22 @@ async function resolveOpenid4vpAuthorizationRequest(options) {
|
|
|
937
1106
|
omitOriginValidation
|
|
938
1107
|
});
|
|
939
1108
|
}
|
|
940
|
-
|
|
1109
|
+
let clientMetadata;
|
|
1110
|
+
if (!isOpenid4vpAuthorizationRequestDcApi(authRequestPayload) && authRequestPayload.client_metadata_uri) {
|
|
1111
|
+
clientMetadata = await fetchClientMetadata({ clientMetadataUri: authRequestPayload.client_metadata_uri });
|
|
1112
|
+
}
|
|
1113
|
+
const clientMeta = parseClientIdentifier({
|
|
1114
|
+
request: { ...authRequestPayload, client_metadata: clientMetadata ?? authRequestPayload.client_metadata },
|
|
1115
|
+
jar,
|
|
1116
|
+
callbacks,
|
|
1117
|
+
origin
|
|
1118
|
+
});
|
|
941
1119
|
let pex;
|
|
942
1120
|
let dcql;
|
|
943
1121
|
if (authRequestPayload.presentation_definition || authRequestPayload.presentation_definition_uri) {
|
|
944
1122
|
if (authRequestPayload.presentation_definition_uri) {
|
|
945
|
-
throw new
|
|
946
|
-
error:
|
|
1123
|
+
throw new Oauth2ServerErrorResponseError10({
|
|
1124
|
+
error: Oauth2ErrorCodes9.InvalidRequest,
|
|
947
1125
|
error_description: "Cannot fetch presentation definition from URI. Not supported."
|
|
948
1126
|
});
|
|
949
1127
|
}
|
|
@@ -958,7 +1136,7 @@ async function resolveOpenid4vpAuthorizationRequest(options) {
|
|
|
958
1136
|
const transactionData = authRequestPayload.transaction_data ? parseTransactionData({ transactionData: authRequestPayload.transaction_data }) : void 0;
|
|
959
1137
|
return {
|
|
960
1138
|
transactionData,
|
|
961
|
-
|
|
1139
|
+
requestPayload: authRequestPayload,
|
|
962
1140
|
jar,
|
|
963
1141
|
client: { ...clientMeta },
|
|
964
1142
|
pex,
|
|
@@ -983,8 +1161,8 @@ function validateOpenId4vpPayload(options) {
|
|
|
983
1161
|
// src/authorization-response/create-authorization-response.ts
|
|
984
1162
|
import {
|
|
985
1163
|
Oauth2Error as Oauth2Error7,
|
|
986
|
-
Oauth2ErrorCodes as
|
|
987
|
-
Oauth2ServerErrorResponseError as
|
|
1164
|
+
Oauth2ErrorCodes as Oauth2ErrorCodes10,
|
|
1165
|
+
Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError11
|
|
988
1166
|
} from "@openid4vc/oauth2";
|
|
989
1167
|
import { dateToSeconds as dateToSeconds2 } from "@openid4vc/utils";
|
|
990
1168
|
|
|
@@ -1033,7 +1211,7 @@ function extractJwksFromClientMetadata(clientMetadata) {
|
|
|
1033
1211
|
}
|
|
1034
1212
|
|
|
1035
1213
|
// src/jarm/jarm-response-mode.ts
|
|
1036
|
-
import { z as
|
|
1214
|
+
import { z as z16 } from "zod";
|
|
1037
1215
|
var jarmResponseMode = [
|
|
1038
1216
|
"jwt",
|
|
1039
1217
|
"query.jwt",
|
|
@@ -1042,7 +1220,7 @@ var jarmResponseMode = [
|
|
|
1042
1220
|
"direct_post.jwt",
|
|
1043
1221
|
"dc_api.jwt"
|
|
1044
1222
|
];
|
|
1045
|
-
var zJarmResponseMode =
|
|
1223
|
+
var zJarmResponseMode = z16.enum(jarmResponseMode);
|
|
1046
1224
|
var isJarmResponseMode = (responseMode) => {
|
|
1047
1225
|
return jarmResponseMode.includes(responseMode);
|
|
1048
1226
|
};
|
|
@@ -1088,55 +1266,55 @@ function jarmAssertMetadataSupported(options) {
|
|
|
1088
1266
|
|
|
1089
1267
|
// src/authorization-response/create-authorization-response.ts
|
|
1090
1268
|
async function createOpenid4vpAuthorizationResponse(options) {
|
|
1091
|
-
const {
|
|
1092
|
-
const
|
|
1093
|
-
...
|
|
1094
|
-
..."state" in
|
|
1269
|
+
const { requestPayload, jarm, callbacks } = options;
|
|
1270
|
+
const responsePayload = {
|
|
1271
|
+
...options.responsePayload,
|
|
1272
|
+
..."state" in requestPayload && { state: requestPayload.state }
|
|
1095
1273
|
};
|
|
1096
|
-
if (
|
|
1274
|
+
if (requestPayload.response_mode && isJarmResponseMode(requestPayload.response_mode) && !jarm) {
|
|
1097
1275
|
throw new Oauth2Error7(
|
|
1098
|
-
`Missing jarm options for creating Jarm response with response mode '${
|
|
1276
|
+
`Missing jarm options for creating Jarm response with response mode '${requestPayload.response_mode}'`
|
|
1099
1277
|
);
|
|
1100
1278
|
}
|
|
1101
1279
|
if (!jarm) {
|
|
1102
1280
|
return {
|
|
1103
|
-
|
|
1281
|
+
responsePayload
|
|
1104
1282
|
};
|
|
1105
1283
|
}
|
|
1106
|
-
if (!
|
|
1284
|
+
if (!requestPayload.client_metadata) {
|
|
1107
1285
|
throw new Oauth2Error7("Missing client metadata in the request params to assert Jarm metadata support.");
|
|
1108
1286
|
}
|
|
1109
|
-
if (!
|
|
1110
|
-
throw new
|
|
1111
|
-
error:
|
|
1287
|
+
if (!requestPayload.client_metadata.jwks) {
|
|
1288
|
+
throw new Oauth2ServerErrorResponseError11({
|
|
1289
|
+
error: Oauth2ErrorCodes10.InvalidRequest,
|
|
1112
1290
|
error_description: "Missing JWKS in client metadata. Cannot extract encryption JWK."
|
|
1113
1291
|
});
|
|
1114
1292
|
}
|
|
1115
1293
|
const supportedJarmMetadata = jarmAssertMetadataSupported({
|
|
1116
|
-
clientMetadata:
|
|
1294
|
+
clientMetadata: requestPayload.client_metadata,
|
|
1117
1295
|
serverMetadata: jarm.serverMetadata
|
|
1118
1296
|
});
|
|
1119
1297
|
const clientMetaJwks = extractJwksFromClientMetadata({
|
|
1120
|
-
...
|
|
1121
|
-
jwks:
|
|
1298
|
+
...requestPayload.client_metadata,
|
|
1299
|
+
jwks: requestPayload.client_metadata.jwks
|
|
1122
1300
|
});
|
|
1123
1301
|
if (!clientMetaJwks?.encJwk) {
|
|
1124
|
-
throw new
|
|
1125
|
-
error:
|
|
1302
|
+
throw new Oauth2ServerErrorResponseError11({
|
|
1303
|
+
error: Oauth2ErrorCodes10.InvalidRequest,
|
|
1126
1304
|
error_description: "Could not extract encryption JWK from client metadata. Failed to create JARM response."
|
|
1127
1305
|
});
|
|
1128
1306
|
}
|
|
1129
1307
|
let additionalJwtPayload;
|
|
1130
1308
|
if (jarm?.jwtSigner) {
|
|
1131
1309
|
if (!jarm.authorizationServer) {
|
|
1132
|
-
throw new
|
|
1133
|
-
error:
|
|
1310
|
+
throw new Oauth2ServerErrorResponseError11({
|
|
1311
|
+
error: Oauth2ErrorCodes10.InvalidRequest,
|
|
1134
1312
|
error_description: "Missing required iss in JARM configuration for creating OpenID4VP authorization response."
|
|
1135
1313
|
});
|
|
1136
1314
|
}
|
|
1137
1315
|
if (!jarm.audience) {
|
|
1138
|
-
throw new
|
|
1139
|
-
error:
|
|
1316
|
+
throw new Oauth2ServerErrorResponseError11({
|
|
1317
|
+
error: Oauth2ErrorCodes10.InvalidRequest,
|
|
1140
1318
|
error_description: "Missing required aud in JARM configuration for creating OpenID4VP authorization response."
|
|
1141
1319
|
});
|
|
1142
1320
|
}
|
|
@@ -1147,18 +1325,18 @@ async function createOpenid4vpAuthorizationResponse(options) {
|
|
|
1147
1325
|
// default: 10 minutes
|
|
1148
1326
|
};
|
|
1149
1327
|
}
|
|
1150
|
-
const
|
|
1151
|
-
...
|
|
1328
|
+
const jarmResponsePayload = {
|
|
1329
|
+
...responsePayload,
|
|
1152
1330
|
...additionalJwtPayload
|
|
1153
1331
|
};
|
|
1154
1332
|
const result = await createJarmAuthResponse({
|
|
1155
|
-
jarmAuthResponse:
|
|
1333
|
+
jarmAuthResponse: jarmResponsePayload,
|
|
1156
1334
|
jwtSigner: jarm?.jwtSigner,
|
|
1157
1335
|
jweEncryptor: jarm?.encryption && (supportedJarmMetadata.type === "encrypt" || supportedJarmMetadata.type === "sign_encrypt") ? {
|
|
1158
1336
|
method: "jwk",
|
|
1159
1337
|
publicJwk: clientMetaJwks.encJwk,
|
|
1160
1338
|
apu: jarm.encryption?.nonce,
|
|
1161
|
-
apv:
|
|
1339
|
+
apv: requestPayload.nonce,
|
|
1162
1340
|
alg: supportedJarmMetadata.client_metadata.authorization_encrypted_response_alg,
|
|
1163
1341
|
enc: supportedJarmMetadata.client_metadata.authorization_encrypted_response_enc
|
|
1164
1342
|
} : void 0,
|
|
@@ -1168,19 +1346,19 @@ async function createOpenid4vpAuthorizationResponse(options) {
|
|
|
1168
1346
|
}
|
|
1169
1347
|
});
|
|
1170
1348
|
return {
|
|
1171
|
-
|
|
1349
|
+
responsePayload: jarmResponsePayload,
|
|
1172
1350
|
jarm: { responseJwt: result.jarmAuthResponseJwt }
|
|
1173
1351
|
};
|
|
1174
1352
|
}
|
|
1175
1353
|
|
|
1176
1354
|
// src/authorization-response/submit-authorization-response.ts
|
|
1177
1355
|
import { Oauth2Error as Oauth2Error9 } from "@openid4vc/oauth2";
|
|
1178
|
-
import { ContentType as
|
|
1356
|
+
import { ContentType as ContentType4, defaultFetcher as defaultFetcher2 } from "@openid4vc/utils";
|
|
1179
1357
|
import { objectToQueryParams as objectToQueryParams3 } from "@openid4vc/utils";
|
|
1180
1358
|
|
|
1181
1359
|
// src/jarm/jarm-auth-response-send.ts
|
|
1182
1360
|
import { Oauth2Error as Oauth2Error8 } from "@openid4vc/oauth2";
|
|
1183
|
-
import { ContentType as
|
|
1361
|
+
import { ContentType as ContentType3, URL as URL3, defaultFetcher } from "@openid4vc/utils";
|
|
1184
1362
|
var jarmAuthResponseSend = (options) => {
|
|
1185
1363
|
const { authRequest, jarmAuthResponseJwt, callbacks } = options;
|
|
1186
1364
|
const responseEndpoint = authRequest.response_uri ?? authRequest.redirect_uri;
|
|
@@ -1193,7 +1371,7 @@ var jarmAuthResponseSend = (options) => {
|
|
|
1193
1371
|
async function handleDirectPostJwt(responseEndpoint, responseJwt, callbacks) {
|
|
1194
1372
|
const response = await (callbacks.fetch ?? defaultFetcher)(responseEndpoint, {
|
|
1195
1373
|
method: "POST",
|
|
1196
|
-
headers: { "Content-Type":
|
|
1374
|
+
headers: { "Content-Type": ContentType3.XWwwFormUrlencoded },
|
|
1197
1375
|
body: `response=${responseJwt}`
|
|
1198
1376
|
});
|
|
1199
1377
|
return {
|
|
@@ -1204,11 +1382,11 @@ async function handleDirectPostJwt(responseEndpoint, responseJwt, callbacks) {
|
|
|
1204
1382
|
|
|
1205
1383
|
// src/authorization-response/submit-authorization-response.ts
|
|
1206
1384
|
async function submitOpenid4vpAuthorizationResponse(options) {
|
|
1207
|
-
const {
|
|
1208
|
-
const url =
|
|
1385
|
+
const { requestPayload, responsePayload, jarm, callbacks } = options;
|
|
1386
|
+
const url = requestPayload.response_uri;
|
|
1209
1387
|
if (jarm) {
|
|
1210
1388
|
return jarmAuthResponseSend({
|
|
1211
|
-
authRequest:
|
|
1389
|
+
authRequest: requestPayload,
|
|
1212
1390
|
jarmAuthResponseJwt: jarm.responseJwt,
|
|
1213
1391
|
callbacks
|
|
1214
1392
|
});
|
|
@@ -1219,12 +1397,12 @@ async function submitOpenid4vpAuthorizationResponse(options) {
|
|
|
1219
1397
|
);
|
|
1220
1398
|
}
|
|
1221
1399
|
const fetch = callbacks.fetch ?? defaultFetcher2;
|
|
1222
|
-
const encodedResponse = objectToQueryParams3(
|
|
1400
|
+
const encodedResponse = objectToQueryParams3(responsePayload);
|
|
1223
1401
|
const submissionResponse = await fetch(url, {
|
|
1224
1402
|
method: "POST",
|
|
1225
1403
|
body: encodedResponse,
|
|
1226
1404
|
headers: {
|
|
1227
|
-
"Content-Type":
|
|
1405
|
+
"Content-Type": ContentType4.XWwwFormUrlencoded
|
|
1228
1406
|
}
|
|
1229
1407
|
});
|
|
1230
1408
|
return {
|
|
@@ -1240,7 +1418,7 @@ import { Oauth2Error as Oauth2Error11 } from "@openid4vc/oauth2";
|
|
|
1240
1418
|
import { Oauth2Error as Oauth2Error10, decodeJwt as decodeJwt4 } from "@openid4vc/oauth2";
|
|
1241
1419
|
import { zCompactJwt as zCompactJwt3 } from "@openid4vc/oauth2";
|
|
1242
1420
|
import { isObject, parseIfJson as parseIfJson2, parseWithErrorHandling as parseWithErrorHandling5 } from "@openid4vc/utils";
|
|
1243
|
-
import { z as
|
|
1421
|
+
import { z as z17 } from "zod";
|
|
1244
1422
|
function parsePresentationsFromVpToken(options) {
|
|
1245
1423
|
const { vpToken: _vpToken } = options;
|
|
1246
1424
|
const vpToken = parseIfJson2(_vpToken);
|
|
@@ -1260,7 +1438,7 @@ function parsePresentationsFromVpToken(options) {
|
|
|
1260
1438
|
function parseDcqlPresentationFromVpToken(options) {
|
|
1261
1439
|
const { vpToken: _vpToken } = options;
|
|
1262
1440
|
const vpToken = parseIfJson2(_vpToken);
|
|
1263
|
-
const parsed = parseWithErrorHandling5(
|
|
1441
|
+
const parsed = parseWithErrorHandling5(z17.object({}).passthrough(), vpToken);
|
|
1264
1442
|
const dcqlPresentationRecord = Object.fromEntries(
|
|
1265
1443
|
Object.entries(parsed).map(([key, value]) => {
|
|
1266
1444
|
return [key, parseSinglePresentationFromVpToken({ vpToken: value })];
|
|
@@ -1271,11 +1449,11 @@ function parseDcqlPresentationFromVpToken(options) {
|
|
|
1271
1449
|
function parseSinglePresentationFromVpToken(options) {
|
|
1272
1450
|
const { vpToken: _vpToken, path } = options;
|
|
1273
1451
|
const vpToken = parseIfJson2(_vpToken);
|
|
1274
|
-
const zLdpVpProof =
|
|
1275
|
-
const ldpVpParseResult =
|
|
1276
|
-
"@context":
|
|
1277
|
-
verifiableCredential:
|
|
1278
|
-
proof:
|
|
1452
|
+
const zLdpVpProof = z17.object({ challenge: z17.string().optional() }).passthrough();
|
|
1453
|
+
const ldpVpParseResult = z17.object({
|
|
1454
|
+
"@context": z17.string().optional(),
|
|
1455
|
+
verifiableCredential: z17.string().optional(),
|
|
1456
|
+
proof: z17.union([zLdpVpProof, z17.array(zLdpVpProof)]).optional()
|
|
1279
1457
|
}).passthrough().safeParse(vpToken);
|
|
1280
1458
|
if (ldpVpParseResult.success && (ldpVpParseResult.data["@context"] || ldpVpParseResult.data.verifiableCredential)) {
|
|
1281
1459
|
const challenge = Array.isArray(ldpVpParseResult.data.proof) ? ldpVpParseResult.data.proof.map((proof) => proof.challenge) : ldpVpParseResult.data.proof?.challenge;
|
|
@@ -1352,61 +1530,61 @@ function parseSinglePresentationFromVpToken(options) {
|
|
|
1352
1530
|
|
|
1353
1531
|
// src/authorization-response/validate-authorization-response.ts
|
|
1354
1532
|
function validateOpenid4vpAuthorizationResponse(options) {
|
|
1355
|
-
const {
|
|
1356
|
-
if (!
|
|
1533
|
+
const { requestPayload, responsePayload } = options;
|
|
1534
|
+
if (!responsePayload.vp_token) {
|
|
1357
1535
|
throw new Oauth2Error11("Failed to verify OpenId4Vp Authorization Response. vp_token is missing.");
|
|
1358
1536
|
}
|
|
1359
|
-
if ("state" in
|
|
1537
|
+
if ("state" in requestPayload && requestPayload.state !== responsePayload.state) {
|
|
1360
1538
|
throw new Oauth2Error11("OpenId4Vp Authorization Response state mismatch.");
|
|
1361
1539
|
}
|
|
1362
|
-
if (
|
|
1540
|
+
if (responsePayload.id_token) {
|
|
1363
1541
|
throw new Oauth2Error11("OpenId4Vp Authorization Response id_token is not supported.");
|
|
1364
1542
|
}
|
|
1365
|
-
if (
|
|
1366
|
-
if (!
|
|
1543
|
+
if (responsePayload.presentation_submission) {
|
|
1544
|
+
if (!requestPayload.presentation_definition) {
|
|
1367
1545
|
throw new Oauth2Error11("OpenId4Vp Authorization Request is missing the required presentation_definition.");
|
|
1368
1546
|
}
|
|
1369
|
-
const presentations = parsePresentationsFromVpToken({ vpToken:
|
|
1370
|
-
if (presentations.every((p) => p.nonce) && !presentations.every((p) => p.nonce ===
|
|
1547
|
+
const presentations = parsePresentationsFromVpToken({ vpToken: responsePayload.vp_token });
|
|
1548
|
+
if (presentations.every((p) => p.nonce) && !presentations.every((p) => p.nonce === requestPayload.nonce)) {
|
|
1371
1549
|
throw new Oauth2Error11(
|
|
1372
1550
|
"Presentation nonce mismatch. The nonce of some presentations does not match the nonce of the request."
|
|
1373
1551
|
);
|
|
1374
1552
|
}
|
|
1375
1553
|
return {
|
|
1376
1554
|
type: "pex",
|
|
1377
|
-
pex: "scope" in
|
|
1378
|
-
scope:
|
|
1379
|
-
presentationSubmission:
|
|
1555
|
+
pex: "scope" in requestPayload && requestPayload.scope ? {
|
|
1556
|
+
scope: requestPayload.scope,
|
|
1557
|
+
presentationSubmission: responsePayload.presentation_submission,
|
|
1380
1558
|
presentations
|
|
1381
1559
|
} : {
|
|
1382
|
-
presentationDefinition:
|
|
1383
|
-
presentationSubmission:
|
|
1560
|
+
presentationDefinition: requestPayload.presentation_definition,
|
|
1561
|
+
presentationSubmission: responsePayload.presentation_submission,
|
|
1384
1562
|
presentations
|
|
1385
1563
|
}
|
|
1386
1564
|
};
|
|
1387
1565
|
}
|
|
1388
|
-
if (
|
|
1389
|
-
if (Array.isArray(
|
|
1566
|
+
if (requestPayload.dcql_query) {
|
|
1567
|
+
if (Array.isArray(responsePayload.vp_token)) {
|
|
1390
1568
|
throw new Oauth2Error11(
|
|
1391
1569
|
"The OpenId4Vp Authorization Response contains multiple vp_token values. In combination with dcql this is not possible."
|
|
1392
1570
|
);
|
|
1393
1571
|
}
|
|
1394
|
-
if (typeof
|
|
1572
|
+
if (typeof responsePayload.vp_token !== "string" && typeof responsePayload.vp_token !== "object") {
|
|
1395
1573
|
throw new Oauth2Error11("With DCQL the vp_token must be a JSON-encoded object.");
|
|
1396
1574
|
}
|
|
1397
|
-
const presentation = parseDcqlPresentationFromVpToken({ vpToken:
|
|
1398
|
-
if (Object.values(presentation).every((p) => p.nonce) && !Object.values(presentation).every((p) => p.nonce ===
|
|
1575
|
+
const presentation = parseDcqlPresentationFromVpToken({ vpToken: responsePayload.vp_token });
|
|
1576
|
+
if (Object.values(presentation).every((p) => p.nonce) && !Object.values(presentation).every((p) => p.nonce === requestPayload.nonce)) {
|
|
1399
1577
|
throw new Oauth2Error11(
|
|
1400
1578
|
"Presentation nonce mismatch. The nonce of some presentations does not match the nonce of the request."
|
|
1401
1579
|
);
|
|
1402
1580
|
}
|
|
1403
1581
|
return {
|
|
1404
1582
|
type: "dcql",
|
|
1405
|
-
dcql: "scope" in
|
|
1406
|
-
scope:
|
|
1583
|
+
dcql: "scope" in requestPayload && requestPayload.scope ? {
|
|
1584
|
+
scope: requestPayload.scope,
|
|
1407
1585
|
presentation
|
|
1408
1586
|
} : {
|
|
1409
|
-
query:
|
|
1587
|
+
query: requestPayload.dcql_query,
|
|
1410
1588
|
presentation
|
|
1411
1589
|
}
|
|
1412
1590
|
};
|
|
@@ -1417,28 +1595,28 @@ function validateOpenid4vpAuthorizationResponse(options) {
|
|
|
1417
1595
|
}
|
|
1418
1596
|
|
|
1419
1597
|
// src/authorization-response/parse-authorization-response.ts
|
|
1420
|
-
import { Oauth2Error as Oauth2Error13, Oauth2ServerErrorResponseError as
|
|
1598
|
+
import { Oauth2Error as Oauth2Error13, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError12 } from "@openid4vc/oauth2";
|
|
1421
1599
|
|
|
1422
1600
|
// src/authorization-response/parse-authorization-response-payload.ts
|
|
1423
1601
|
import { parseWithErrorHandling as parseWithErrorHandling6 } from "@openid4vc/utils";
|
|
1424
1602
|
|
|
1425
1603
|
// src/authorization-response/z-authorization-response.ts
|
|
1426
|
-
import { z as
|
|
1604
|
+
import { z as z19 } from "zod";
|
|
1427
1605
|
|
|
1428
1606
|
// src/vp-token/z-vp-token.ts
|
|
1429
|
-
import { z as
|
|
1430
|
-
var zVpToken =
|
|
1607
|
+
import { z as z18 } from "zod";
|
|
1608
|
+
var zVpToken = z18.union([z18.string(), z18.array(z18.union([z18.string(), z18.record(z18.any())])), z18.record(z18.any())]);
|
|
1431
1609
|
|
|
1432
1610
|
// src/authorization-response/z-authorization-response.ts
|
|
1433
|
-
var zOpenid4vpAuthorizationResponse =
|
|
1434
|
-
state:
|
|
1435
|
-
id_token:
|
|
1611
|
+
var zOpenid4vpAuthorizationResponse = z19.object({
|
|
1612
|
+
state: z19.string().optional(),
|
|
1613
|
+
id_token: z19.string().optional(),
|
|
1436
1614
|
vp_token: zVpToken,
|
|
1437
|
-
presentation_submission:
|
|
1438
|
-
refresh_token:
|
|
1439
|
-
token_type:
|
|
1440
|
-
access_token:
|
|
1441
|
-
expires_in:
|
|
1615
|
+
presentation_submission: z19.unknown().optional(),
|
|
1616
|
+
refresh_token: z19.string().optional(),
|
|
1617
|
+
token_type: z19.string().optional(),
|
|
1618
|
+
access_token: z19.string().optional(),
|
|
1619
|
+
expires_in: z19.number().optional()
|
|
1442
1620
|
}).passthrough();
|
|
1443
1621
|
|
|
1444
1622
|
// src/authorization-response/parse-authorization-response-payload.ts
|
|
@@ -1459,16 +1637,16 @@ import {
|
|
|
1459
1637
|
zJwtHeader as zJwtHeader2
|
|
1460
1638
|
} from "@openid4vc/oauth2";
|
|
1461
1639
|
import { decodeBase64 as decodeBase642, encodeToUtf8String as encodeToUtf8String2, parseWithErrorHandling as parseWithErrorHandling7 } from "@openid4vc/utils";
|
|
1462
|
-
import
|
|
1640
|
+
import z20 from "zod";
|
|
1463
1641
|
async function parseJarmAuthorizationResponse(options) {
|
|
1464
1642
|
const { jarmResponseJwt, callbacks } = options;
|
|
1465
1643
|
const jarmAuthorizationResponseJwt = parseWithErrorHandling7(
|
|
1466
|
-
|
|
1644
|
+
z20.union([zCompactJwt4, zCompactJwe3]),
|
|
1467
1645
|
jarmResponseJwt,
|
|
1468
1646
|
"Invalid jarm authorization response jwt."
|
|
1469
1647
|
);
|
|
1470
1648
|
const verifiedJarmResponse = await verifyJarmAuthorizationResponse({ jarmAuthorizationResponseJwt, callbacks });
|
|
1471
|
-
const zJarmHeader =
|
|
1649
|
+
const zJarmHeader = z20.object({ ...zJwtHeader2.shape, apu: z20.string().optional(), apv: z20.string().optional() });
|
|
1472
1650
|
const { header: jarmHeader } = decodeJwtHeader2({
|
|
1473
1651
|
jwt: jarmAuthorizationResponseJwt,
|
|
1474
1652
|
headerSchema: zJarmHeader
|
|
@@ -1481,8 +1659,8 @@ async function parseJarmAuthorizationResponse(options) {
|
|
|
1481
1659
|
}
|
|
1482
1660
|
const authResponsePayload = parseOpenid4VpAuthorizationResponsePayload(verifiedJarmResponse.jarmAuthResponse);
|
|
1483
1661
|
const validateOpenId4vpResponse = validateOpenid4vpAuthorizationResponse({
|
|
1484
|
-
|
|
1485
|
-
|
|
1662
|
+
requestPayload: parsedAuthorizationRequest.params,
|
|
1663
|
+
responsePayload: authResponsePayload
|
|
1486
1664
|
});
|
|
1487
1665
|
const authRequestPayload = parsedAuthorizationRequest.params;
|
|
1488
1666
|
if (!authRequestPayload.response_mode || !isJarmResponseMode(authRequestPayload.response_mode)) {
|
|
@@ -1522,11 +1700,11 @@ async function parseOpenid4vpAuthorizationResponse(options) {
|
|
|
1522
1700
|
}
|
|
1523
1701
|
const authRequestPayload = parsedAuthRequest.params;
|
|
1524
1702
|
const validateOpenId4vpResponse = validateOpenid4vpAuthorizationResponse({
|
|
1525
|
-
|
|
1526
|
-
|
|
1703
|
+
requestPayload: authRequestPayload,
|
|
1704
|
+
responsePayload: authResponsePayload
|
|
1527
1705
|
});
|
|
1528
1706
|
if (authRequestPayload.response_mode && isJarmResponseMode(authRequestPayload.response_mode)) {
|
|
1529
|
-
throw new
|
|
1707
|
+
throw new Oauth2ServerErrorResponseError12(
|
|
1530
1708
|
{
|
|
1531
1709
|
error: "invalid_request",
|
|
1532
1710
|
error_description: "Invalid response mode for openid4vp response. Expected jarm response."
|
|
@@ -1589,23 +1767,12 @@ var Openid4vpVerifier = class {
|
|
|
1589
1767
|
};
|
|
1590
1768
|
|
|
1591
1769
|
// src/models/z-credential-formats.ts
|
|
1592
|
-
import { z as z20 } from "zod";
|
|
1593
|
-
var zCredentialFormat = z20.enum(["jwt_vc_json", "ldp_vc", "ac_vc", "mso_mdoc", "dc+sd-jwt"]);
|
|
1594
|
-
|
|
1595
|
-
// src/models/z-proof-formats.ts
|
|
1596
1770
|
import { z as z21 } from "zod";
|
|
1597
|
-
var
|
|
1771
|
+
var zCredentialFormat = z21.enum(["jwt_vc_json", "ldp_vc", "ac_vc", "mso_mdoc", "dc+sd-jwt", "vc+sd-jwt"]);
|
|
1598
1772
|
|
|
1599
|
-
// src/models/z-
|
|
1773
|
+
// src/models/z-proof-formats.ts
|
|
1600
1774
|
import { z as z22 } from "zod";
|
|
1601
|
-
var
|
|
1602
|
-
presentation_definition_uri_supported: z22.optional(z22.boolean()),
|
|
1603
|
-
vp_formats_supported: zVpFormatsSupported,
|
|
1604
|
-
client_id_schemes_supported: z22.optional(z22.array(zClientIdScheme)),
|
|
1605
|
-
request_object_signing_alg_values_supported: z22.optional(z22.array(z22.string())),
|
|
1606
|
-
authorization_encryption_alg_values_supported: z22.optional(z22.array(z22.string())),
|
|
1607
|
-
authorization_encryption_enc_values_supported: z22.optional(z22.array(z22.string()))
|
|
1608
|
-
});
|
|
1775
|
+
var zProofFormat = z22.enum(["jwt_vp_json", "ldc_vp", "ac_vp", "dc+sd-jwt", "vc+sd-jwt", "mso_mdoc"]);
|
|
1609
1776
|
export {
|
|
1610
1777
|
Openid4vpClient,
|
|
1611
1778
|
Openid4vpVerifier,
|