@openid4vc/oauth2 0.3.0-alpha-20250324183425 → 0.3.0-alpha-20250328112257

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.mjs CHANGED
@@ -429,6 +429,303 @@ var zCompactJwe = z6.string().regex(/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*\.[A-Za-z0-9
429
429
  message: "Not a valid compact jwe"
430
430
  });
431
431
 
432
+ // src/client-attestation/clent-attestation.ts
433
+ import { dateToSeconds as dateToSeconds3, parseWithErrorHandling as parseWithErrorHandling5 } from "@openid4vc/utils";
434
+
435
+ // src/common/jwt/verify-jwt.ts
436
+ import { dateToSeconds } from "@openid4vc/utils";
437
+
438
+ // src/error/Oauth2JwtVerificationError.ts
439
+ var Oauth2JwtVerificationError = class extends Oauth2Error {
440
+ constructor(message, options) {
441
+ const errorMessage = message ?? "Error verifiying jwt.";
442
+ super(errorMessage, options);
443
+ }
444
+ };
445
+
446
+ // src/common/jwt/verify-jwt.ts
447
+ async function verifyJwt(options) {
448
+ const errorMessage = options.errorMessage ?? "Error during verification of jwt.";
449
+ let signerJwk;
450
+ try {
451
+ const result = await options.verifyJwtCallback(options.signer, {
452
+ header: options.header,
453
+ payload: options.payload,
454
+ compact: options.compact
455
+ });
456
+ if (!result.verified) throw new Oauth2JwtVerificationError(errorMessage);
457
+ signerJwk = result.signerJwk;
458
+ } catch (error) {
459
+ if (error instanceof Oauth2JwtVerificationError) throw error;
460
+ throw new Oauth2JwtVerificationError(errorMessage, { cause: error });
461
+ }
462
+ const nowInSeconds = dateToSeconds(options.now ?? /* @__PURE__ */ new Date());
463
+ const skewInSeconds = options.allowedSkewInSeconds ?? 0;
464
+ const timeBasedValidation = options.skipTimeBasedValidation !== void 0 ? !options.skipTimeBasedValidation : true;
465
+ if (timeBasedValidation && options.payload.nbf && nowInSeconds < options.payload.nbf - skewInSeconds) {
466
+ throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'nbf' is in the future`);
467
+ }
468
+ if (timeBasedValidation && options.payload.exp && nowInSeconds > options.payload.exp + skewInSeconds) {
469
+ throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'exp' is in the past`);
470
+ }
471
+ if (options.expectedAudience && options.expectedAudience !== options.payload.aud) {
472
+ throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'aud' does not match expected value.`);
473
+ }
474
+ if (options.expectedIssuer && options.expectedIssuer !== options.payload.iss) {
475
+ throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'iss' does not match expected value.`);
476
+ }
477
+ if (options.expectedNonce && options.expectedNonce !== options.payload.nonce) {
478
+ throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'nonce' does not match expected value.`);
479
+ }
480
+ if (options.expectedSubject && options.expectedSubject !== options.payload.sub) {
481
+ throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'sub' does not match expected value.`);
482
+ }
483
+ if (options.requiredClaims) {
484
+ for (const claim of options.requiredClaims) {
485
+ if (!options.payload[claim]) {
486
+ throw new Oauth2JwtVerificationError(`${errorMessage} jwt '${claim}' is missing.`);
487
+ }
488
+ }
489
+ }
490
+ return {
491
+ signer: {
492
+ ...options.signer,
493
+ publicJwk: signerJwk
494
+ }
495
+ };
496
+ }
497
+
498
+ // src/error/Oauth2ServerErrorResponseError.ts
499
+ var Oauth2ServerErrorResponseError = class extends Oauth2Error {
500
+ constructor(errorResponse, options) {
501
+ super(
502
+ `${options?.internalMessage ?? errorResponse.error_description}
503
+ ${JSON.stringify(errorResponse, null, 2)}`,
504
+ options
505
+ );
506
+ this.errorResponse = errorResponse;
507
+ this.status = options?.status ?? 400;
508
+ }
509
+ };
510
+
511
+ // src/client-attestation/client-attestation-pop.ts
512
+ import { addSecondsToDate, dateToSeconds as dateToSeconds2, encodeToBase64Url as encodeToBase64Url2, parseWithErrorHandling as parseWithErrorHandling4 } from "@openid4vc/utils";
513
+
514
+ // src/client-attestation/z-client-attestation.ts
515
+ import { zHttpsUrl, zInteger as zInteger2 } from "@openid4vc/utils";
516
+ import z7 from "zod";
517
+ var zOauthClientAttestationHeader = z7.literal("OAuth-Client-Attestation");
518
+ var oauthClientAttestationHeader = zOauthClientAttestationHeader.value;
519
+ var zClientAttestationJwtPayload = z7.object({
520
+ ...zJwtPayload.shape,
521
+ iss: z7.string(),
522
+ sub: z7.string(),
523
+ exp: zInteger2,
524
+ cnf: z7.object({
525
+ jwk: zJwk
526
+ }).passthrough(),
527
+ // OID4VCI Wallet Attestation Extensions
528
+ wallet_name: z7.string().optional(),
529
+ wallet_link: z7.string().url().optional()
530
+ }).passthrough();
531
+ var zClientAttestationJwtHeader = z7.object({
532
+ ...zJwtHeader.shape,
533
+ typ: z7.literal("oauth-client-attestation+jwt")
534
+ }).passthrough();
535
+ var zOauthClientAttestationPopHeader = z7.literal("OAuth-Client-Attestation-PoP");
536
+ var oauthClientAttestationPopHeader = zOauthClientAttestationPopHeader.value;
537
+ var zClientAttestationPopJwtPayload = z7.object({
538
+ ...zJwtPayload.shape,
539
+ iss: z7.string(),
540
+ exp: zInteger2,
541
+ aud: zHttpsUrl,
542
+ jti: z7.string(),
543
+ nonce: z7.optional(z7.string())
544
+ }).passthrough();
545
+ var zClientAttestationPopJwtHeader = z7.object({
546
+ ...zJwtHeader.shape,
547
+ typ: z7.literal("oauth-client-attestation-pop+jwt")
548
+ }).passthrough();
549
+
550
+ // src/client-attestation/client-attestation-pop.ts
551
+ async function verifyClientAttestationPopJwt(options) {
552
+ const { header, payload } = decodeJwt({
553
+ jwt: options.clientAttestationPopJwt,
554
+ headerSchema: zClientAttestationPopJwtHeader,
555
+ payloadSchema: zClientAttestationPopJwtPayload
556
+ });
557
+ if (payload.iss !== options.clientAttestation.payload.sub) {
558
+ throw new Oauth2Error(
559
+ `Client Attestation Pop jwt contains 'iss' (client_id) value '${payload.iss}', but expected 'sub' value from client attestation '${options.clientAttestation.payload.sub}'`
560
+ );
561
+ }
562
+ if (payload.aud !== options.authorizationServer) {
563
+ throw new Oauth2Error(
564
+ `Client Attestation Pop jwt contains 'aud' value '${payload.aud}', but expected authorization server identifier '${options.authorizationServer}'`
565
+ );
566
+ }
567
+ const { signer } = await verifyJwt({
568
+ signer: {
569
+ alg: header.alg,
570
+ method: "jwk",
571
+ publicJwk: options.clientAttestation.payload.cnf.jwk
572
+ },
573
+ now: options.now,
574
+ header,
575
+ expectedNonce: options.expectedNonce,
576
+ payload,
577
+ compact: options.clientAttestationPopJwt,
578
+ verifyJwtCallback: options.callbacks.verifyJwt,
579
+ errorMessage: "client attestation pop jwt verification failed"
580
+ });
581
+ return {
582
+ header,
583
+ payload,
584
+ signer
585
+ };
586
+ }
587
+ async function createClientAttestationPopJwt(options) {
588
+ const clientAttestation = decodeJwt({
589
+ jwt: options.clientAttestation,
590
+ headerSchema: zClientAttestationJwtHeader,
591
+ payloadSchema: zClientAttestationJwtPayload
592
+ });
593
+ const signer = options.signer ?? {
594
+ method: "jwk",
595
+ alg: clientAttestation.header.alg,
596
+ publicJwk: clientAttestation.payload.cnf.jwk
597
+ };
598
+ const header = parseWithErrorHandling4(zClientAttestationPopJwtHeader, {
599
+ typ: "oauth-client-attestation-pop+jwt",
600
+ alg: signer.alg
601
+ });
602
+ const expiresAt = options.expiresAt ?? addSecondsToDate(options.issuedAt ?? /* @__PURE__ */ new Date(), 1 * 60);
603
+ const payload = parseWithErrorHandling4(zClientAttestationPopJwtPayload, {
604
+ aud: options.authorizationServer,
605
+ iss: clientAttestation.payload.sub,
606
+ iat: dateToSeconds2(options.issuedAt),
607
+ exp: dateToSeconds2(expiresAt),
608
+ jti: encodeToBase64Url2(await options.callbacks.generateRandom(32)),
609
+ nonce: options.nonce,
610
+ ...options.additionalPayload
611
+ });
612
+ const { jwt } = await options.callbacks.signJwt(signer, {
613
+ header,
614
+ payload
615
+ });
616
+ return jwt;
617
+ }
618
+
619
+ // src/client-attestation/clent-attestation.ts
620
+ async function verifyClientAttestationJwt(options) {
621
+ const { header, payload } = decodeJwt({
622
+ jwt: options.clientAttestationJwt,
623
+ headerSchema: zClientAttestationJwtHeader,
624
+ payloadSchema: zClientAttestationJwtPayload
625
+ });
626
+ const { signer } = await verifyJwt({
627
+ signer: jwtSignerFromJwt({ header, payload }),
628
+ now: options.now,
629
+ header,
630
+ payload,
631
+ compact: options.clientAttestationJwt,
632
+ verifyJwtCallback: options.callbacks.verifyJwt,
633
+ errorMessage: "client attestation jwt verification failed"
634
+ });
635
+ return {
636
+ header,
637
+ payload,
638
+ signer
639
+ };
640
+ }
641
+ async function createClientAttestationJwt(options) {
642
+ const header = parseWithErrorHandling5(zClientAttestationJwtHeader, {
643
+ typ: "oauth-client-attestation+jwt",
644
+ ...jwtHeaderFromJwtSigner(options.signer)
645
+ });
646
+ const payload = parseWithErrorHandling5(zClientAttestationJwtPayload, {
647
+ iss: options.issuer,
648
+ iat: dateToSeconds3(options.issuedAt),
649
+ exp: dateToSeconds3(options.expiresAt),
650
+ sub: options.clientId,
651
+ cnf: options.confirmation,
652
+ ...options.additionalPayload
653
+ });
654
+ const { jwt } = await options.callbacks.signJwt(options.signer, {
655
+ header,
656
+ payload
657
+ });
658
+ return jwt;
659
+ }
660
+ function extractClientAttestationJwtsFromHeaders(headers) {
661
+ const clientAttestationHeader = headers.get(oauthClientAttestationHeader);
662
+ const clientAttestationPopHeader = headers.get(oauthClientAttestationPopHeader);
663
+ if (!clientAttestationHeader && !clientAttestationPopHeader) {
664
+ return { valid: true };
665
+ }
666
+ if (!clientAttestationHeader || !clientAttestationPopHeader) {
667
+ return { valid: false };
668
+ }
669
+ if (!zCompactJwt.safeParse(clientAttestationHeader).success || !zCompactJwt.safeParse(clientAttestationPopHeader).success) {
670
+ return { valid: false };
671
+ }
672
+ return {
673
+ valid: true,
674
+ clientAttestationPopHeader,
675
+ clientAttestationHeader
676
+ };
677
+ }
678
+ async function verifyClientAttestation({
679
+ authorizationServer,
680
+ clientAttestationJwt,
681
+ clientAttestationPopJwt,
682
+ callbacks,
683
+ now
684
+ }) {
685
+ try {
686
+ const clientAttestation = await verifyClientAttestationJwt({
687
+ callbacks,
688
+ clientAttestationJwt,
689
+ now
690
+ });
691
+ const clientAttestationPop = await verifyClientAttestationPopJwt({
692
+ callbacks,
693
+ authorizationServer,
694
+ clientAttestation,
695
+ clientAttestationPopJwt,
696
+ now
697
+ });
698
+ return {
699
+ clientAttestation,
700
+ clientAttestationPop
701
+ };
702
+ } catch (error) {
703
+ if (error instanceof Oauth2Error) {
704
+ throw new Oauth2ServerErrorResponseError(
705
+ {
706
+ error: "invalid_client" /* InvalidClient */,
707
+ error_description: `Error verifying client attestation. ${error.message}`
708
+ },
709
+ {
710
+ status: 401,
711
+ cause: error
712
+ }
713
+ );
714
+ }
715
+ throw new Oauth2ServerErrorResponseError(
716
+ {
717
+ error: "server_error" /* ServerError */,
718
+ error_description: "Error during verification of client attestation jwt"
719
+ },
720
+ {
721
+ status: 500,
722
+ cause: error,
723
+ internalMessage: "Unknown error thrown during verification of client attestation jwt"
724
+ }
725
+ );
726
+ }
727
+ }
728
+
432
729
  // src/index.ts
433
730
  import { InvalidFetchResponseError as InvalidFetchResponseError7 } from "@openid4vc/utils";
434
731
 
@@ -450,14 +747,6 @@ var Oauth2ClientAuthorizationChallengeError = class extends Oauth2ClientErrorRes
450
747
  }
451
748
  };
452
749
 
453
- // src/error/Oauth2JwtVerificationError.ts
454
- var Oauth2JwtVerificationError = class extends Oauth2Error {
455
- constructor(message, options) {
456
- const errorMessage = message ?? "Error verifiying jwt.";
457
- super(errorMessage, options);
458
- }
459
- };
460
-
461
750
  // src/error/Oauth2ResourceUnauthorizedError.ts
462
751
  import { encodeWwwAuthenticateHeader, parseWwwAuthenticateHeader } from "@openid4vc/utils";
463
752
  var Oauth2ResourceUnauthorizedError = class _Oauth2ResourceUnauthorizedError extends Oauth2Error {
@@ -496,19 +785,6 @@ ${JSON.stringify(wwwAuthenticateHeaders, null, 2)}`);
496
785
  }
497
786
  };
498
787
 
499
- // src/error/Oauth2ServerErrorResponseError.ts
500
- var Oauth2ServerErrorResponseError = class extends Oauth2Error {
501
- constructor(errorResponse, options) {
502
- super(
503
- `${options?.internalMessage ?? errorResponse.error_description}
504
- ${JSON.stringify(errorResponse, null, 2)}`,
505
- options
506
- );
507
- this.errorResponse = errorResponse;
508
- this.status = options?.status ?? 400;
509
- }
510
- };
511
-
512
788
  // src/metadata/authorization-server/authorization-server-metadata.ts
513
789
  import { URL, joinUriParts } from "@openid4vc/utils";
514
790
 
@@ -516,7 +792,7 @@ import { URL, joinUriParts } from "@openid4vc/utils";
516
792
  import { ContentType, createZodFetcher } from "@openid4vc/utils";
517
793
  import { InvalidFetchResponseError } from "@openid4vc/utils";
518
794
 
519
- // ../utils/src/error/ValidationError.ts
795
+ // ../utils/src/zod-error.ts
520
796
  import { ZodIssueCode } from "zod";
521
797
  var constants = {
522
798
  // biome-ignore lint/suspicious/noMisleadingCharacterClass: expected
@@ -524,69 +800,71 @@ var constants = {
524
800
  unionSeparator: ", or ",
525
801
  issueSeparator: "\n - "
526
802
  };
527
- var ValidationError = class extends Error {
528
- escapeQuotes(str) {
529
- return str.replace(/"/g, '\\"');
803
+ function escapeQuotes(str) {
804
+ return str.replace(/"/g, '\\"');
805
+ }
806
+ function joinPath(path) {
807
+ if (path.length === 1) {
808
+ return path[0].toString();
530
809
  }
531
- joinPath(path) {
532
- if (path.length === 1) {
533
- return path[0].toString();
534
- }
535
- return path.reduce((acc, item) => {
536
- if (typeof item === "number") {
537
- return `${acc}[${item.toString()}]`;
538
- }
539
- if (item.includes('"')) {
540
- return `${acc}["${this.escapeQuotes(item)}"]`;
541
- }
542
- if (!constants.identifierRegex.test(item)) {
543
- return `${acc}["${item}"]`;
544
- }
545
- const separator = acc.length === 0 ? "" : ".";
546
- return acc + separator + item;
547
- }, "");
548
- }
549
- getMessageFromUnionErrors(unionErrors) {
550
- return unionErrors.reduce((acc, zodError) => {
551
- const newIssues = zodError.issues.map((issue) => this.getMessageFromZodIssue(issue)).join(constants.issueSeparator);
552
- if (!acc.includes(newIssues)) acc.push(newIssues);
553
- return acc;
554
- }, []).join(constants.unionSeparator);
555
- }
556
- getMessageFromZodIssue(issue) {
557
- if (issue.code === ZodIssueCode.invalid_union) {
558
- return this.getMessageFromUnionErrors(issue.unionErrors);
810
+ return path.reduce((acc, item) => {
811
+ if (typeof item === "number") {
812
+ return `${acc}[${item.toString()}]`;
559
813
  }
560
- if (issue.code === ZodIssueCode.invalid_arguments) {
561
- return [issue.message, ...issue.argumentsError.issues.map((issue2) => this.getMessageFromZodIssue(issue2))].join(
562
- constants.issueSeparator
563
- );
814
+ if (item.includes('"')) {
815
+ return `${acc}["${escapeQuotes(item)}"]`;
564
816
  }
565
- if (issue.code === ZodIssueCode.invalid_return_type) {
566
- return [issue.message, ...issue.returnTypeError.issues.map((issue2) => this.getMessageFromZodIssue(issue2))].join(
567
- constants.issueSeparator
568
- );
817
+ if (!constants.identifierRegex.test(item)) {
818
+ return `${acc}["${item}"]`;
569
819
  }
570
- if (issue.path.length !== 0) {
571
- if (issue.path.length === 1) {
572
- const identifier = issue.path[0];
573
- if (typeof identifier === "number") {
574
- return `${issue.message} at index ${identifier}`;
575
- }
820
+ const separator = acc.length === 0 ? "" : ".";
821
+ return acc + separator + item;
822
+ }, "");
823
+ }
824
+ function getMessageFromZodIssue(issue) {
825
+ if (issue.code === ZodIssueCode.invalid_union) {
826
+ return getMessageFromUnionErrors(issue.unionErrors);
827
+ }
828
+ if (issue.code === ZodIssueCode.invalid_arguments) {
829
+ return [issue.message, ...issue.argumentsError.issues.map((issue2) => getMessageFromZodIssue(issue2))].join(
830
+ constants.issueSeparator
831
+ );
832
+ }
833
+ if (issue.code === ZodIssueCode.invalid_return_type) {
834
+ return [issue.message, ...issue.returnTypeError.issues.map((issue2) => getMessageFromZodIssue(issue2))].join(
835
+ constants.issueSeparator
836
+ );
837
+ }
838
+ if (issue.path.length !== 0) {
839
+ if (issue.path.length === 1) {
840
+ const identifier = issue.path[0];
841
+ if (typeof identifier === "number") {
842
+ return `${issue.message} at index ${identifier}`;
576
843
  }
577
- return `${issue.message} at "${this.joinPath(issue.path)}"`;
578
844
  }
579
- return issue.message;
580
- }
581
- formatError(error) {
582
- if (!error) return "";
583
- return error?.issues.map((issue) => this.getMessageFromZodIssue(issue)).join(constants.issueSeparator);
845
+ return `${issue.message} at "${joinPath(issue.path)}"`;
584
846
  }
847
+ return issue.message;
848
+ }
849
+ function getMessageFromUnionErrors(unionErrors) {
850
+ return unionErrors.reduce((acc, zodError) => {
851
+ const newIssues = zodError.issues.map((issue) => getMessageFromZodIssue(issue)).join(constants.issueSeparator);
852
+ if (!acc.includes(newIssues)) acc.push(newIssues);
853
+ return acc;
854
+ }, []).join(constants.unionSeparator);
855
+ }
856
+ function formatZodError(error) {
857
+ if (!error) return "";
858
+ return ` - ${error?.issues.map((issue) => getMessageFromZodIssue(issue)).join(constants.issueSeparator)}`;
859
+ }
860
+
861
+ // ../utils/src/error/ValidationError.ts
862
+ var ValidationError = class extends Error {
585
863
  constructor(message, zodError) {
586
864
  super(message);
587
- const formattedError = this.formatError(zodError);
865
+ const formattedError = formatZodError(zodError);
588
866
  this.message = `${message}
589
- - ${formattedError}`;
867
+ ${formattedError}`;
590
868
  Object.defineProperty(this, "zodError", {
591
869
  value: zodError,
592
870
  writable: false,
@@ -616,31 +894,40 @@ async function fetchWellKnownMetadata(wellKnownMetadataUrl, schema, fetch) {
616
894
  }
617
895
 
618
896
  // src/metadata/authorization-server/z-authorization-server-metadata.ts
619
- import { zHttpsUrl } from "@openid4vc/utils";
620
- import z7 from "zod";
621
- var zAuthorizationServerMetadata = z7.object({
622
- issuer: zHttpsUrl,
623
- token_endpoint: zHttpsUrl,
624
- token_endpoint_auth_methods_supported: z7.optional(z7.array(z7.string())),
625
- authorization_endpoint: z7.optional(zHttpsUrl),
626
- jwks_uri: z7.optional(zHttpsUrl),
897
+ import { zHttpsUrl as zHttpsUrl2 } from "@openid4vc/utils";
898
+ import z8 from "zod";
899
+ var knownClientAuthenticationMethod = z8.enum([
900
+ "client_secret_basic",
901
+ "client_secret_post",
902
+ "attest_jwt_client_auth",
903
+ "client_secret_jwt",
904
+ "private_key_jwt"
905
+ ]);
906
+ var zAuthorizationServerMetadata = z8.object({
907
+ issuer: zHttpsUrl2,
908
+ token_endpoint: zHttpsUrl2,
909
+ token_endpoint_auth_methods_supported: z8.optional(z8.array(z8.union([knownClientAuthenticationMethod, z8.string()]))),
910
+ authorization_endpoint: z8.optional(zHttpsUrl2),
911
+ jwks_uri: z8.optional(zHttpsUrl2),
627
912
  // RFC7636
628
- code_challenge_methods_supported: z7.optional(z7.array(z7.string())),
913
+ code_challenge_methods_supported: z8.optional(z8.array(z8.string())),
629
914
  // RFC9449
630
- dpop_signing_alg_values_supported: z7.optional(z7.array(z7.string())),
915
+ dpop_signing_alg_values_supported: z8.optional(z8.array(z8.string())),
631
916
  // RFC9126
632
- require_pushed_authorization_requests: z7.optional(z7.boolean()),
633
- pushed_authorization_request_endpoint: z7.optional(zHttpsUrl),
917
+ require_pushed_authorization_requests: z8.optional(z8.boolean()),
918
+ pushed_authorization_request_endpoint: z8.optional(zHttpsUrl2),
634
919
  // RFC9068
635
- introspection_endpoint: z7.optional(zHttpsUrl),
636
- introspection_endpoint_auth_methods_supported: z7.optional(
637
- z7.array(z7.union([z7.literal("client_secret_jwt"), z7.literal("private_key_jwt"), z7.string()]))
920
+ introspection_endpoint: z8.optional(zHttpsUrl2),
921
+ introspection_endpoint_auth_methods_supported: z8.optional(
922
+ z8.array(z8.union([knownClientAuthenticationMethod, z8.string()]))
638
923
  ),
639
- introspection_endpoint_auth_signing_alg_values_supported: z7.optional(z7.array(zAlgValueNotNone)),
924
+ introspection_endpoint_auth_signing_alg_values_supported: z8.optional(z8.array(zAlgValueNotNone)),
640
925
  // FiPA (no RFC yet)
641
- authorization_challenge_endpoint: z7.optional(zHttpsUrl),
926
+ authorization_challenge_endpoint: z8.optional(zHttpsUrl2),
642
927
  // From OpenID4VCI specification
643
- pre_authorized_grant_anonymous_access_supported: z7.optional(z7.boolean())
928
+ pre_authorized_grant_anonymous_access_supported: z8.optional(z8.boolean()),
929
+ // Attestation Based Client Auth (draft 5)
930
+ client_attestation_pop_nonce_required: z8.boolean().optional()
644
931
  }).passthrough().refine(
645
932
  ({
646
933
  introspection_endpoint_auth_methods_supported: methodsSupported,
@@ -736,78 +1023,25 @@ async function fetchJwks(jwksUrl, fetch) {
736
1023
  return result.data;
737
1024
  }
738
1025
 
739
- // src/common/jwt/verify-jwt.ts
740
- import { dateToSeconds } from "@openid4vc/utils";
741
- async function verifyJwt(options) {
742
- const errorMessage = options.errorMessage ?? "Error during verification of jwt.";
743
- let signerJwk;
744
- try {
745
- const result = await options.verifyJwtCallback(options.signer, {
746
- header: options.header,
747
- payload: options.payload,
748
- compact: options.compact
749
- });
750
- if (!result.verified) throw new Oauth2JwtVerificationError(errorMessage);
751
- signerJwk = result.signerJwk;
752
- } catch (error) {
753
- if (error instanceof Oauth2JwtVerificationError) throw error;
754
- throw new Oauth2JwtVerificationError(errorMessage, { cause: error });
755
- }
756
- const nowInSeconds = dateToSeconds(options.now ?? /* @__PURE__ */ new Date());
757
- const skewInSeconds = options.allowedSkewInSeconds ?? 0;
758
- const timeBasedValidation = options.skipTimeBasedValidation !== void 0 ? !options.skipTimeBasedValidation : true;
759
- if (timeBasedValidation && options.payload.nbf && nowInSeconds < options.payload.nbf - skewInSeconds) {
760
- throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'nbf' is in the future`);
761
- }
762
- if (timeBasedValidation && options.payload.exp && nowInSeconds > options.payload.exp + skewInSeconds) {
763
- throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'exp' is in the past`);
764
- }
765
- if (options.expectedAudience && options.expectedAudience !== options.payload.aud) {
766
- throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'aud' does not match expected value.`);
767
- }
768
- if (options.expectedIssuer && options.expectedIssuer !== options.payload.iss) {
769
- throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'iss' does not match expected value.`);
770
- }
771
- if (options.expectedNonce && options.expectedNonce !== options.payload.nonce) {
772
- throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'nonce' does not match expected value.`);
773
- }
774
- if (options.expectedSubject && options.expectedSubject !== options.payload.sub) {
775
- throw new Oauth2JwtVerificationError(`${errorMessage} jwt 'sub' does not match expected value.`);
776
- }
777
- if (options.requiredClaims) {
778
- for (const claim of options.requiredClaims) {
779
- if (!options.payload[claim]) {
780
- throw new Oauth2JwtVerificationError(`${errorMessage} jwt '${claim}' is missing.`);
781
- }
782
- }
783
- }
784
- return {
785
- signer: {
786
- ...options.signer,
787
- publicJwk: signerJwk
788
- }
789
- };
790
- }
791
-
792
1026
  // src/access-token/z-access-token-jwt.ts
793
- import { zInteger as zInteger2 } from "@openid4vc/utils";
794
- import z8 from "zod";
795
- var zAccessTokenProfileJwtHeader = z8.object({
1027
+ import { zInteger as zInteger3 } from "@openid4vc/utils";
1028
+ import z9 from "zod";
1029
+ var zAccessTokenProfileJwtHeader = z9.object({
796
1030
  ...zJwtHeader.shape,
797
- typ: z8.enum(["application/at+jwt", "at+jwt"])
1031
+ typ: z9.enum(["application/at+jwt", "at+jwt"])
798
1032
  }).passthrough();
799
- var zAccessTokenProfileJwtPayload = z8.object({
1033
+ var zAccessTokenProfileJwtPayload = z9.object({
800
1034
  ...zJwtPayload.shape,
801
- iss: z8.string(),
802
- exp: zInteger2,
803
- iat: zInteger2,
804
- aud: z8.string(),
805
- sub: z8.string(),
1035
+ iss: z9.string(),
1036
+ exp: zInteger3,
1037
+ iat: zInteger3,
1038
+ aud: z9.string(),
1039
+ sub: z9.string(),
806
1040
  // REQUIRED according to RFC 9068, but OpenID4VCI allows anonymous access
807
- client_id: z8.optional(z8.string()),
808
- jti: z8.string(),
1041
+ client_id: z9.optional(z9.string()),
1042
+ jti: z9.string(),
809
1043
  // SHOULD be included in the authorization request contained it
810
- scope: z8.optional(z8.string())
1044
+ scope: z9.optional(z9.string())
811
1045
  }).passthrough();
812
1046
 
813
1047
  // src/access-token/verify-access-token.ts
@@ -863,27 +1097,27 @@ import { createFetcher } from "@openid4vc/utils";
863
1097
  // src/dpop/dpop.ts
864
1098
  import {
865
1099
  URL as URL2,
866
- dateToSeconds as dateToSeconds2,
1100
+ dateToSeconds as dateToSeconds4,
867
1101
  decodeUtf8String as decodeUtf8String2,
868
- encodeToBase64Url as encodeToBase64Url2,
869
- parseWithErrorHandling as parseWithErrorHandling4
1102
+ encodeToBase64Url as encodeToBase64Url3,
1103
+ parseWithErrorHandling as parseWithErrorHandling6
870
1104
  } from "@openid4vc/utils";
871
1105
 
872
1106
  // src/dpop/z-dpop.ts
873
- import { zHttpMethod, zHttpsUrl as zHttpsUrl2, zInteger as zInteger3 } from "@openid4vc/utils";
874
- import z9 from "zod";
875
- var zDpopJwtPayload = z9.object({
1107
+ import { zHttpMethod, zHttpsUrl as zHttpsUrl3, zInteger as zInteger4 } from "@openid4vc/utils";
1108
+ import z10 from "zod";
1109
+ var zDpopJwtPayload = z10.object({
876
1110
  ...zJwtPayload.shape,
877
- iat: zInteger3,
878
- htu: zHttpsUrl2,
1111
+ iat: zInteger4,
1112
+ htu: zHttpsUrl3,
879
1113
  htm: zHttpMethod,
880
- jti: z9.string(),
1114
+ jti: z10.string(),
881
1115
  // Only required when presenting in combination with access token
882
- ath: z9.optional(z9.string())
1116
+ ath: z10.optional(z10.string())
883
1117
  }).passthrough();
884
- var zDpopJwtHeader = z9.object({
1118
+ var zDpopJwtHeader = z10.object({
885
1119
  ...zJwtHeader.shape,
886
- typ: z9.literal("dpop+jwt"),
1120
+ typ: z10.literal("dpop+jwt"),
887
1121
  jwk: zJwk
888
1122
  }).passthrough();
889
1123
 
@@ -897,18 +1131,18 @@ async function createDpopHeadersForRequest(options) {
897
1131
  async function createDpopJwt(options) {
898
1132
  let ath = void 0;
899
1133
  if (options.accessToken) {
900
- ath = encodeToBase64Url2(await options.callbacks.hash(decodeUtf8String2(options.accessToken), "sha-256" /* Sha256 */));
1134
+ ath = encodeToBase64Url3(await options.callbacks.hash(decodeUtf8String2(options.accessToken), "sha-256" /* Sha256 */));
901
1135
  }
902
- const header = parseWithErrorHandling4(zDpopJwtHeader, {
1136
+ const header = parseWithErrorHandling6(zDpopJwtHeader, {
903
1137
  typ: "dpop+jwt",
904
1138
  jwk: options.signer.publicJwk,
905
1139
  alg: options.signer.alg
906
1140
  });
907
- const payload = parseWithErrorHandling4(zDpopJwtPayload, {
1141
+ const payload = parseWithErrorHandling6(zDpopJwtPayload, {
908
1142
  htu: htuFromRequestUrl(options.request.url),
909
- iat: dateToSeconds2(options.issuedAt),
1143
+ iat: dateToSeconds4(options.issuedAt),
910
1144
  htm: options.request.method,
911
- jti: encodeToBase64Url2(await options.callbacks.generateRandom(32)),
1145
+ jti: encodeToBase64Url3(await options.callbacks.generateRandom(32)),
912
1146
  ath,
913
1147
  nonce: options.nonce,
914
1148
  ...options.additionalPayload
@@ -920,75 +1154,86 @@ async function createDpopJwt(options) {
920
1154
  return jwt;
921
1155
  }
922
1156
  async function verifyDpopJwt(options) {
923
- const { header, payload } = decodeJwt({
924
- jwt: options.dpopJwt,
925
- headerSchema: zDpopJwtHeader,
926
- payloadSchema: zDpopJwtPayload
927
- });
928
- if (options.allowedSigningAlgs && !options.allowedSigningAlgs.includes(header.alg)) {
929
- throw new Oauth2Error(
930
- `dpop jwt uses alg value '${header.alg}' but allowed dpop signging alg values are ${options.allowedSigningAlgs.join(", ")}.`
931
- );
932
- }
933
- if (options.expectedNonce) {
934
- if (!payload.nonce) {
935
- throw new Oauth2Error(`Dpop jwt does not have a nonce value, but expected nonce value '${options.expectedNonce}'`);
1157
+ try {
1158
+ const { header, payload } = decodeJwt({
1159
+ jwt: options.dpopJwt,
1160
+ headerSchema: zDpopJwtHeader,
1161
+ payloadSchema: zDpopJwtPayload
1162
+ });
1163
+ if (options.allowedSigningAlgs && !options.allowedSigningAlgs.includes(header.alg)) {
1164
+ throw new Oauth2Error(
1165
+ `dpop jwt uses alg value '${header.alg}' but allowed dpop signging alg values are ${options.allowedSigningAlgs.join(", ")}.`
1166
+ );
1167
+ }
1168
+ if (options.expectedNonce) {
1169
+ if (!payload.nonce) {
1170
+ throw new Oauth2Error(
1171
+ `Dpop jwt does not have a nonce value, but expected nonce value '${options.expectedNonce}'`
1172
+ );
1173
+ }
1174
+ if (payload.nonce !== options.expectedNonce) {
1175
+ throw new Oauth2Error(
1176
+ `Dpop jwt contains nonce value '${payload.nonce}', but expected nonce value '${options.expectedNonce}'`
1177
+ );
1178
+ }
936
1179
  }
937
- if (payload.nonce !== options.expectedNonce) {
1180
+ if (options.request.method !== payload.htm) {
938
1181
  throw new Oauth2Error(
939
- `Dpop jwt contains nonce value '${payload.nonce}', but expected nonce value '${options.expectedNonce}'`
1182
+ `Dpop jwt contains htm value '${payload.htm}', but expected htm value '${options.request.method}'`
940
1183
  );
941
1184
  }
942
- }
943
- if (options.request.method !== payload.htm) {
944
- throw new Oauth2Error(
945
- `Dpop jwt contains htm value '${payload.htm}', but expected htm value '${options.request.method}'`
946
- );
947
- }
948
- const expectedHtu = htuFromRequestUrl(options.request.url);
949
- if (expectedHtu !== payload.htu) {
950
- throw new Oauth2Error(`Dpop jwt contains htu value '${payload.htu}', but expected htu value '${expectedHtu}'.`);
951
- }
952
- if (options.accessToken) {
953
- const expectedAth = encodeToBase64Url2(
954
- await options.callbacks.hash(decodeUtf8String2(options.accessToken), "sha-256" /* Sha256 */)
955
- );
956
- if (!payload.ath) {
957
- throw new Oauth2Error(`Dpop jwt does not have a ath value, but expected ath value '${expectedAth}'.`);
1185
+ const expectedHtu = htuFromRequestUrl(options.request.url);
1186
+ if (expectedHtu !== payload.htu) {
1187
+ throw new Oauth2Error(`Dpop jwt contains htu value '${payload.htu}', but expected htu value '${expectedHtu}'.`);
958
1188
  }
959
- if (payload.ath !== expectedAth) {
960
- throw new Oauth2Error(`Dpop jwt contains ath value '${payload.ath}', but expected ath value '${expectedAth}'.`);
1189
+ if (options.accessToken) {
1190
+ const expectedAth = encodeToBase64Url3(
1191
+ await options.callbacks.hash(decodeUtf8String2(options.accessToken), "sha-256" /* Sha256 */)
1192
+ );
1193
+ if (!payload.ath) {
1194
+ throw new Oauth2Error(`Dpop jwt does not have a ath value, but expected ath value '${expectedAth}'.`);
1195
+ }
1196
+ if (payload.ath !== expectedAth) {
1197
+ throw new Oauth2Error(`Dpop jwt contains ath value '${payload.ath}', but expected ath value '${expectedAth}'.`);
1198
+ }
961
1199
  }
962
- }
963
- if (options.expectedJwkThumbprint) {
964
- const jwkThumprint = await calculateJwkThumbprint({
1200
+ const jwkThumbprint = await calculateJwkThumbprint({
965
1201
  hashAlgorithm: "sha-256" /* Sha256 */,
966
1202
  hashCallback: options.callbacks.hash,
967
1203
  jwk: header.jwk
968
1204
  });
969
- if (options.expectedJwkThumbprint !== jwkThumprint) {
1205
+ if (options.expectedJwkThumbprint && options.expectedJwkThumbprint !== jwkThumbprint) {
970
1206
  throw new Oauth2Error(
971
- `Dpop is signed with jwk with thumbprint value '${jwkThumprint}', but expect jwk thumbprint value '${options.expectedJwkThumbprint}'`
1207
+ `Dpop is signed with jwk with thumbprint value '${jwkThumbprint}', but expect jwk thumbprint value '${options.expectedJwkThumbprint}'`
972
1208
  );
973
1209
  }
1210
+ await verifyJwt({
1211
+ signer: {
1212
+ alg: header.alg,
1213
+ method: "jwk",
1214
+ publicJwk: header.jwk
1215
+ },
1216
+ now: options.now,
1217
+ header,
1218
+ payload,
1219
+ compact: options.dpopJwt,
1220
+ verifyJwtCallback: options.callbacks.verifyJwt,
1221
+ errorMessage: "dpop jwt verification failed"
1222
+ });
1223
+ return {
1224
+ header,
1225
+ payload,
1226
+ jwkThumbprint
1227
+ };
1228
+ } catch (error) {
1229
+ if (error instanceof Oauth2Error) {
1230
+ throw new Oauth2ServerErrorResponseError({
1231
+ error: "invalid_dpop_proof" /* InvalidDpopProof */,
1232
+ error_description: error.message
1233
+ });
1234
+ }
1235
+ throw error;
974
1236
  }
975
- await verifyJwt({
976
- signer: {
977
- alg: header.alg,
978
- method: "jwk",
979
- publicJwk: header.jwk
980
- },
981
- now: options.now,
982
- header,
983
- payload,
984
- compact: options.dpopJwt,
985
- verifyJwtCallback: options.callbacks.verifyJwt,
986
- errorMessage: "dpop jwt verification failed"
987
- });
988
- return {
989
- header,
990
- payload
991
- };
992
1237
  }
993
1238
  function htuFromRequestUrl(requestUrl) {
994
1239
  const htu = new URL2(requestUrl);
@@ -1001,10 +1246,13 @@ function extractDpopNonceFromHeaders(headers) {
1001
1246
  }
1002
1247
  function extractDpopJwtFromHeaders(headers) {
1003
1248
  const dpopJwt = headers.get("DPoP");
1004
- if (dpopJwt && (typeof dpopJwt !== "string" || dpopJwt.includes(","))) {
1249
+ if (!dpopJwt) {
1250
+ return { valid: true };
1251
+ }
1252
+ if (!zCompactJwt.safeParse(dpopJwt).success) {
1005
1253
  return { valid: false };
1006
1254
  }
1007
- return { valid: true, dpopJwt: dpopJwt ?? void 0 };
1255
+ return { valid: true, dpopJwt };
1008
1256
  }
1009
1257
 
1010
1258
  // src/dpop/dpop-retry.ts
@@ -1126,37 +1374,37 @@ async function resourceRequest(options) {
1126
1374
  import { ValidationError as ValidationError2 } from "@openid4vc/utils";
1127
1375
 
1128
1376
  // src/access-token/introspect-token.ts
1129
- import { ContentType as ContentType3, createZodFetcher as createZodFetcher3, objectToQueryParams, parseWithErrorHandling as parseWithErrorHandling5 } from "@openid4vc/utils";
1377
+ import { ContentType as ContentType3, createZodFetcher as createZodFetcher3, objectToQueryParams, parseWithErrorHandling as parseWithErrorHandling7 } from "@openid4vc/utils";
1130
1378
  import { InvalidFetchResponseError as InvalidFetchResponseError3 } from "@openid4vc/utils";
1131
1379
  import { Headers } from "@openid4vc/utils";
1132
1380
 
1133
1381
  // src/access-token/z-token-introspection.ts
1134
- import { zInteger as zInteger4 } from "@openid4vc/utils";
1135
- import z10 from "zod";
1136
- var zTokenIntrospectionRequest = z10.object({
1137
- token: z10.string(),
1138
- token_type_hint: z10.optional(z10.string())
1382
+ import { zInteger as zInteger5 } from "@openid4vc/utils";
1383
+ import z11 from "zod";
1384
+ var zTokenIntrospectionRequest = z11.object({
1385
+ token: z11.string(),
1386
+ token_type_hint: z11.optional(z11.string())
1139
1387
  }).passthrough();
1140
- var zTokenIntrospectionResponse = z10.object({
1141
- active: z10.boolean(),
1142
- scope: z10.optional(z10.string()),
1143
- client_id: z10.optional(z10.string()),
1144
- username: z10.optional(z10.string()),
1145
- token_type: z10.optional(z10.string()),
1146
- exp: z10.optional(zInteger4),
1147
- iat: z10.optional(zInteger4),
1148
- nbf: z10.optional(zInteger4),
1149
- sub: z10.optional(z10.string()),
1150
- aud: z10.optional(z10.string()),
1151
- iss: z10.optional(z10.string()),
1152
- jti: z10.optional(z10.string()),
1153
- cnf: z10.optional(zJwtConfirmationPayload)
1388
+ var zTokenIntrospectionResponse = z11.object({
1389
+ active: z11.boolean(),
1390
+ scope: z11.optional(z11.string()),
1391
+ client_id: z11.optional(z11.string()),
1392
+ username: z11.optional(z11.string()),
1393
+ token_type: z11.optional(z11.string()),
1394
+ exp: z11.optional(zInteger5),
1395
+ iat: z11.optional(zInteger5),
1396
+ nbf: z11.optional(zInteger5),
1397
+ sub: z11.optional(z11.string()),
1398
+ aud: z11.optional(z11.string()),
1399
+ iss: z11.optional(z11.string()),
1400
+ jti: z11.optional(z11.string()),
1401
+ cnf: z11.optional(zJwtConfirmationPayload)
1154
1402
  }).passthrough();
1155
1403
 
1156
1404
  // src/access-token/introspect-token.ts
1157
1405
  async function introspectToken(options) {
1158
1406
  const fetchWithZod = createZodFetcher3(options.callbacks.fetch);
1159
- const introspectionRequest = parseWithErrorHandling5(zTokenIntrospectionRequest, {
1407
+ const introspectionRequest = parseWithErrorHandling7(zTokenIntrospectionRequest, {
1160
1408
  token: options.token,
1161
1409
  token_type_hint: options.tokenTypeHint,
1162
1410
  ...options.additionalPayload
@@ -1171,7 +1419,7 @@ async function introspectToken(options) {
1171
1419
  await options.callbacks.clientAuthentication({
1172
1420
  url: introspectionEndpoint,
1173
1421
  method: "POST",
1174
- authorizationServerMetata: options.authorizationServerMetadata,
1422
+ authorizationServerMetadata: options.authorizationServerMetadata,
1175
1423
  body: introspectionRequest,
1176
1424
  contentType: ContentType3.XWwwFormUrlencoded,
1177
1425
  headers
@@ -1323,7 +1571,7 @@ async function verifyResourceRequest(options) {
1323
1571
  }
1324
1572
  return {
1325
1573
  tokenPayload,
1326
- dpopJwk,
1574
+ dpop: dpopJwk ? { jwk: dpopJwk } : void 0,
1327
1575
  scheme,
1328
1576
  accessToken,
1329
1577
  authorizationServer: authorizationServer.issuer
@@ -1331,10 +1579,23 @@ async function verifyResourceRequest(options) {
1331
1579
  }
1332
1580
 
1333
1581
  // src/client-authentication.ts
1334
- import { decodeUtf8String as decodeUtf8String3, encodeToBase64Url as encodeToBase64Url3 } from "@openid4vc/utils";
1582
+ import { decodeUtf8String as decodeUtf8String3, encodeToBase64Url as encodeToBase64Url4 } from "@openid4vc/utils";
1583
+
1584
+ // src/z-grant-type.ts
1585
+ import z12 from "zod";
1586
+ var zPreAuthorizedCodeGrantIdentifier = z12.literal("urn:ietf:params:oauth:grant-type:pre-authorized_code");
1587
+ var preAuthorizedCodeGrantIdentifier = zPreAuthorizedCodeGrantIdentifier.value;
1588
+ var zAuthorizationCodeGrantIdentifier = z12.literal("authorization_code");
1589
+ var authorizationCodeGrantIdentifier = zAuthorizationCodeGrantIdentifier.value;
1590
+ var zRefreshTokenGrantIdentifier = z12.literal("refresh_token");
1591
+ var refreshTokenGrantIdentifier = zRefreshTokenGrantIdentifier.value;
1592
+
1593
+ // src/client-authentication.ts
1335
1594
  var SupportedClientAuthenticationMethod = /* @__PURE__ */ ((SupportedClientAuthenticationMethod2) => {
1336
1595
  SupportedClientAuthenticationMethod2["ClientSecretBasic"] = "client_secret_basic";
1337
1596
  SupportedClientAuthenticationMethod2["ClientSecretPost"] = "client_secret_post";
1597
+ SupportedClientAuthenticationMethod2["ClientAttestationJwt"] = "attest_jwt_client_auth";
1598
+ SupportedClientAuthenticationMethod2["None"] = "none";
1338
1599
  return SupportedClientAuthenticationMethod2;
1339
1600
  })(SupportedClientAuthenticationMethod || {});
1340
1601
  function getSupportedClientAuthenticationMethod(authorizationServer, endpointType) {
@@ -1370,15 +1631,21 @@ function getSupportedClientAuthenticationMethod(authorizationServer, endpointTyp
1370
1631
  }
1371
1632
  function clientAuthenticationDynamic(options) {
1372
1633
  return (callbackOptions) => {
1373
- const { url, authorizationServerMetata } = callbackOptions;
1374
- const endpointType = url === authorizationServerMetata.introspection_endpoint ? "introspection" : "endpoint";
1375
- const method = getSupportedClientAuthenticationMethod(authorizationServerMetata, endpointType);
1634
+ const { url, authorizationServerMetadata, body } = callbackOptions;
1635
+ const endpointType = url === authorizationServerMetadata.introspection_endpoint ? "introspection" : url === authorizationServerMetadata.token_endpoint ? "token" : "endpoint";
1636
+ const method = getSupportedClientAuthenticationMethod(authorizationServerMetadata, endpointType);
1637
+ if (endpointType === "token" && body.grant_type === preAuthorizedCodeGrantIdentifier && authorizationServerMetadata.pre_authorized_grant_anonymous_access_supported) {
1638
+ return clientAuthenticationAnonymous()(callbackOptions);
1639
+ }
1376
1640
  if (method === "client_secret_basic" /* ClientSecretBasic */) {
1377
1641
  return clientAuthenticationClientSecretBasic(options)(callbackOptions);
1378
1642
  }
1379
1643
  if (method === "client_secret_post" /* ClientSecretPost */) {
1380
1644
  return clientAuthenticationClientSecretPost(options)(callbackOptions);
1381
1645
  }
1646
+ if (method === "none" /* None */) {
1647
+ return clientAuthenticationNone(options)(callbackOptions);
1648
+ }
1382
1649
  throw new Oauth2Error(
1383
1650
  `Unsupported client auth method ${method}. Supported values are ${Object.values(
1384
1651
  SupportedClientAuthenticationMethod
@@ -1394,40 +1661,61 @@ function clientAuthenticationClientSecretPost(options) {
1394
1661
  }
1395
1662
  function clientAuthenticationClientSecretBasic(options) {
1396
1663
  return ({ headers }) => {
1397
- const authorization = encodeToBase64Url3(decodeUtf8String3(`${options.clientId}:${options.clientSecret}`));
1664
+ const authorization = encodeToBase64Url4(decodeUtf8String3(`${options.clientId}:${options.clientSecret}`));
1398
1665
  headers.set("Authorization", `Basic ${authorization}`);
1399
1666
  };
1400
1667
  }
1401
- function clientAuthenticationNone() {
1668
+ function clientAuthenticationNone(options) {
1669
+ return ({ body }) => {
1670
+ body.client_id = options.clientId;
1671
+ };
1672
+ }
1673
+ function clientAuthenticationAnonymous() {
1402
1674
  return () => {
1403
1675
  };
1404
1676
  }
1677
+ function clientAuthenticationClientAttestationJwt(options) {
1678
+ return async ({ headers, authorizationServerMetadata }) => {
1679
+ const clientAttestationPop = await createClientAttestationPopJwt({
1680
+ authorizationServer: authorizationServerMetadata.issuer,
1681
+ callbacks: options.callbacks,
1682
+ clientAttestation: options.clientAttestationJwt
1683
+ // TODO: support client attestation nonce
1684
+ // We can fetch it before making the request if we don't have a nonce
1685
+ // https://www.ietf.org/archive/id/draft-ietf-oauth-attestation-based-client-auth-05.html
1686
+ // https://github.com/oauth-wg/draft-ietf-oauth-attestation-based-client-auth/issues/101
1687
+ // nonce:
1688
+ });
1689
+ headers.set(oauthClientAttestationHeader, options.clientAttestationJwt);
1690
+ headers.set(oauthClientAttestationPopHeader, clientAttestationPop);
1691
+ };
1692
+ }
1405
1693
 
1406
1694
  // src/Oauth2AuthorizationServer.ts
1407
1695
  import { parseWithErrorHandling as parseWithErrorHandling12 } from "@openid4vc/utils";
1408
1696
 
1409
1697
  // src/access-token/create-access-token.ts
1410
- import { addSecondsToDate, dateToSeconds as dateToSeconds3, encodeToBase64Url as encodeToBase64Url4, parseWithErrorHandling as parseWithErrorHandling6 } from "@openid4vc/utils";
1698
+ import { addSecondsToDate as addSecondsToDate2, dateToSeconds as dateToSeconds5, encodeToBase64Url as encodeToBase64Url5, parseWithErrorHandling as parseWithErrorHandling8 } from "@openid4vc/utils";
1411
1699
  async function createAccessTokenJwt(options) {
1412
- const header = parseWithErrorHandling6(zAccessTokenProfileJwtHeader, {
1700
+ const header = parseWithErrorHandling8(zAccessTokenProfileJwtHeader, {
1413
1701
  ...jwtHeaderFromJwtSigner(options.signer),
1414
1702
  typ: "at+jwt"
1415
1703
  });
1416
1704
  const now = options.now ?? /* @__PURE__ */ new Date();
1417
- const payload = parseWithErrorHandling6(zAccessTokenProfileJwtPayload, {
1418
- iat: dateToSeconds3(now),
1419
- exp: dateToSeconds3(addSecondsToDate(now, options.expiresInSeconds)),
1705
+ const payload = parseWithErrorHandling8(zAccessTokenProfileJwtPayload, {
1706
+ iat: dateToSeconds5(now),
1707
+ exp: dateToSeconds5(addSecondsToDate2(now, options.expiresInSeconds)),
1420
1708
  aud: options.audience,
1421
1709
  iss: options.authorizationServer,
1422
- jti: encodeToBase64Url4(await options.callbacks.generateRandom(32)),
1710
+ jti: encodeToBase64Url5(await options.callbacks.generateRandom(32)),
1423
1711
  client_id: options.clientId,
1424
1712
  sub: options.subject,
1425
1713
  scope: options.scope,
1426
- cnf: options.dpopJwk ? {
1714
+ cnf: options.dpop ? {
1427
1715
  jkt: await calculateJwkThumbprint({
1428
1716
  hashAlgorithm: "sha-256" /* Sha256 */,
1429
1717
  hashCallback: options.callbacks.hash,
1430
- jwk: options.dpopJwk
1718
+ jwk: options.dpop.jwk
1431
1719
  })
1432
1720
  } : void 0,
1433
1721
  ...options.additionalPayload
@@ -1442,45 +1730,34 @@ async function createAccessTokenJwt(options) {
1442
1730
  }
1443
1731
 
1444
1732
  // src/access-token/create-access-token-response.ts
1445
- import { parseWithErrorHandling as parseWithErrorHandling7 } from "@openid4vc/utils";
1446
-
1447
- // src/access-token/z-access-token.ts
1448
- import z12 from "zod";
1449
- import { zHttpsUrl as zHttpsUrl3 } from "@openid4vc/utils";
1450
-
1451
- // src/z-grant-type.ts
1452
- import z11 from "zod";
1453
- var zPreAuthorizedCodeGrantIdentifier = z11.literal("urn:ietf:params:oauth:grant-type:pre-authorized_code");
1454
- var preAuthorizedCodeGrantIdentifier = zPreAuthorizedCodeGrantIdentifier.value;
1455
- var zAuthorizationCodeGrantIdentifier = z11.literal("authorization_code");
1456
- var authorizationCodeGrantIdentifier = zAuthorizationCodeGrantIdentifier.value;
1457
- var zRefreshTokenGrantIdentifier = z11.literal("refresh_token");
1458
- var refreshTokenGrantIdentifier = zRefreshTokenGrantIdentifier.value;
1733
+ import { parseWithErrorHandling as parseWithErrorHandling9 } from "@openid4vc/utils";
1459
1734
 
1460
1735
  // src/access-token/z-access-token.ts
1461
- var zAccessTokenRequest = z12.intersection(
1462
- z12.object({
1736
+ import z13 from "zod";
1737
+ import { zHttpsUrl as zHttpsUrl4 } from "@openid4vc/utils";
1738
+ var zAccessTokenRequest = z13.intersection(
1739
+ z13.object({
1463
1740
  // Pre authorized code flow
1464
- "pre-authorized_code": z12.optional(z12.string()),
1741
+ "pre-authorized_code": z13.optional(z13.string()),
1465
1742
  // Authorization code flow
1466
- code: z12.optional(z12.string()),
1467
- redirect_uri: z12.string().url().optional(),
1743
+ code: z13.optional(z13.string()),
1744
+ redirect_uri: z13.string().url().optional(),
1468
1745
  // Refresh token grant
1469
- refresh_token: z12.optional(z12.string()),
1470
- resource: z12.optional(zHttpsUrl3),
1471
- code_verifier: z12.optional(z12.string()),
1472
- grant_type: z12.union([
1746
+ refresh_token: z13.optional(z13.string()),
1747
+ resource: z13.optional(zHttpsUrl4),
1748
+ code_verifier: z13.optional(z13.string()),
1749
+ grant_type: z13.union([
1473
1750
  zPreAuthorizedCodeGrantIdentifier,
1474
1751
  zAuthorizationCodeGrantIdentifier,
1475
1752
  zRefreshTokenGrantIdentifier,
1476
1753
  // string makes the previous ones unessary, but it does help with error messages
1477
- z12.string()
1754
+ z13.string()
1478
1755
  ])
1479
1756
  }).passthrough(),
1480
- z12.object({
1481
- tx_code: z12.optional(z12.string()),
1757
+ z13.object({
1758
+ tx_code: z13.optional(z13.string()),
1482
1759
  // user_pin is from OpenID4VCI draft 11
1483
- user_pin: z12.optional(z12.string())
1760
+ user_pin: z13.optional(z13.string())
1484
1761
  }).passthrough().refine(({ tx_code, user_pin }) => !tx_code || !user_pin || user_pin === tx_code, {
1485
1762
  message: `If both 'tx_code' and 'user_pin' are present they must match`
1486
1763
  }).transform(({ tx_code, user_pin, ...rest }) => {
@@ -1490,20 +1767,20 @@ var zAccessTokenRequest = z12.intersection(
1490
1767
  };
1491
1768
  })
1492
1769
  );
1493
- var zAccessTokenResponse = z12.object({
1494
- access_token: z12.string(),
1495
- token_type: z12.string(),
1496
- expires_in: z12.optional(z12.number().int()),
1497
- scope: z12.optional(z12.string()),
1498
- state: z12.optional(z12.string()),
1499
- refresh_token: z12.optional(z12.string()),
1770
+ var zAccessTokenResponse = z13.object({
1771
+ access_token: z13.string(),
1772
+ token_type: z13.string(),
1773
+ expires_in: z13.optional(z13.number().int()),
1774
+ scope: z13.optional(z13.string()),
1775
+ state: z13.optional(z13.string()),
1776
+ refresh_token: z13.optional(z13.string()),
1500
1777
  // OpenID4VCI specific parameters
1501
- c_nonce: z12.optional(z12.string()),
1502
- c_nonce_expires_in: z12.optional(z12.number().int()),
1778
+ c_nonce: z13.optional(z13.string()),
1779
+ c_nonce_expires_in: z13.optional(z13.number().int()),
1503
1780
  // TODO: add additional params
1504
- authorization_details: z12.array(
1505
- z12.object({
1506
- // requried when type is openid_credential (so we probably need a discriminator)
1781
+ authorization_details: z13.array(
1782
+ z13.object({
1783
+ // required when type is openid_credential (so we probably need a discriminator)
1507
1784
  // credential_identifiers: z.array(z.string()),
1508
1785
  }).passthrough()
1509
1786
  ).optional()
@@ -1512,7 +1789,7 @@ var zAccessTokenErrorResponse = zOauth2ErrorResponse;
1512
1789
 
1513
1790
  // src/access-token/create-access-token-response.ts
1514
1791
  async function createAccessTokenResponse(options) {
1515
- const accessTokenResponse = parseWithErrorHandling7(zAccessTokenResponse, {
1792
+ const accessTokenResponse = parseWithErrorHandling9(zAccessTokenResponse, {
1516
1793
  access_token: options.accessToken,
1517
1794
  token_type: options.tokenType,
1518
1795
  expires_in: options.expiresInSeconds,
@@ -1524,13 +1801,14 @@ async function createAccessTokenResponse(options) {
1524
1801
  }
1525
1802
 
1526
1803
  // src/access-token/parse-access-token-request.ts
1804
+ import { formatZodError as formatZodError2 } from "@openid4vc/utils";
1527
1805
  function parseAccessTokenRequest(options) {
1528
1806
  const parsedAccessTokenRequest = zAccessTokenRequest.safeParse(options.accessTokenRequest);
1529
1807
  if (!parsedAccessTokenRequest.success) {
1530
1808
  throw new Oauth2ServerErrorResponseError({
1531
1809
  error: "invalid_request" /* InvalidRequest */,
1532
1810
  error_description: `Error occured during validation of authorization request.
1533
- ${JSON.stringify(parsedAccessTokenRequest.error.issues, null, 2)}`
1811
+ ${formatZodError2(parsedAccessTokenRequest.error)}`
1534
1812
  });
1535
1813
  }
1536
1814
  const accessTokenRequest = parsedAccessTokenRequest.data;
@@ -1571,17 +1849,30 @@ ${JSON.stringify(parsedAccessTokenRequest.error.issues, null, 2)}`
1571
1849
  error_description: `Request contains a 'DPoP' header, but the value is not a valid DPoP jwt`
1572
1850
  });
1573
1851
  }
1852
+ const extractedClientAttestationJwts = extractClientAttestationJwtsFromHeaders(options.request.headers);
1853
+ if (!extractedClientAttestationJwts.valid) {
1854
+ throw new Oauth2ServerErrorResponseError({
1855
+ error: "invalid_client" /* InvalidClient */,
1856
+ error_description: "Request contains client attestation header, but the values are not valid client attestation and client attestation PoP header."
1857
+ });
1858
+ }
1574
1859
  const pkceCodeVerifier = accessTokenRequest.code_verifier;
1575
1860
  return {
1576
1861
  accessTokenRequest,
1577
1862
  grant,
1578
- dpopJwt: extractedDpopJwt.dpopJwt,
1863
+ dpop: extractedDpopJwt.dpopJwt ? {
1864
+ jwt: extractedDpopJwt.dpopJwt
1865
+ } : void 0,
1866
+ clientAttestation: extractedClientAttestationJwts.clientAttestationHeader ? {
1867
+ clientAttestationJwt: extractedClientAttestationJwts.clientAttestationHeader,
1868
+ clientAttestationPopJwt: extractedClientAttestationJwts.clientAttestationPopHeader
1869
+ } : void 0,
1579
1870
  pkceCodeVerifier
1580
1871
  };
1581
1872
  }
1582
1873
 
1583
1874
  // src/pkce.ts
1584
- import { decodeUtf8String as decodeUtf8String4, encodeToBase64Url as encodeToBase64Url5 } from "@openid4vc/utils";
1875
+ import { decodeUtf8String as decodeUtf8String4, encodeToBase64Url as encodeToBase64Url6 } from "@openid4vc/utils";
1585
1876
  var PkceCodeChallengeMethod = /* @__PURE__ */ ((PkceCodeChallengeMethod2) => {
1586
1877
  PkceCodeChallengeMethod2["Plain"] = "plain";
1587
1878
  PkceCodeChallengeMethod2["S256"] = "S256";
@@ -1596,7 +1887,7 @@ async function createPkce(options) {
1596
1887
  throw new Oauth2Error(`Unable to create PKCE code verifier. 'allowedCodeChallengeMethods' is an empty array.`);
1597
1888
  }
1598
1889
  const codeChallengeMethod = allowedCodeChallengeMethods.includes("S256" /* S256 */) ? "S256" /* S256 */ : "plain" /* Plain */;
1599
- const codeVerifier = options.codeVerifier ?? encodeToBase64Url5(await options.callbacks.generateRandom(64));
1890
+ const codeVerifier = options.codeVerifier ?? encodeToBase64Url6(await options.callbacks.generateRandom(64));
1600
1891
  return {
1601
1892
  codeVerifier,
1602
1893
  codeChallenge: await calculateCodeChallenge({
@@ -1624,13 +1915,24 @@ async function calculateCodeChallenge(options) {
1624
1915
  return options.codeVerifier;
1625
1916
  }
1626
1917
  if (options.codeChallengeMethod === "S256" /* S256 */) {
1627
- return encodeToBase64Url5(await options.hashCallback(decodeUtf8String4(options.codeVerifier), "sha-256" /* Sha256 */));
1918
+ return encodeToBase64Url6(await options.hashCallback(decodeUtf8String4(options.codeVerifier), "sha-256" /* Sha256 */));
1628
1919
  }
1629
1920
  throw new Oauth2Error(`Unsupported code challenge method ${options.codeChallengeMethod}`);
1630
1921
  }
1631
1922
 
1632
1923
  // src/access-token/verify-access-token-request.ts
1633
1924
  async function verifyPreAuthorizedCodeAccessTokenRequest(options) {
1925
+ if (options.pkce) {
1926
+ await verifyAccessTokenRequestPkce(options.pkce, options.callbacks);
1927
+ }
1928
+ const dpopResult = options.dpop ? await verifyAccessTokenRequestDpop(options.dpop, options.request, options.callbacks) : void 0;
1929
+ const clientAttestationResult = options.clientAttestation ? await verifyAccessTokenRequestClientAttestation(
1930
+ options.clientAttestation,
1931
+ options.authorizationServerMetadata,
1932
+ options.callbacks,
1933
+ dpopResult?.jwkThumbprint,
1934
+ options.now
1935
+ ) : void 0;
1634
1936
  if (options.grant.preAuthorizedCode !== options.expectedPreAuthorizedCode) {
1635
1937
  throw new Oauth2ServerErrorResponseError({
1636
1938
  error: "invalid_grant" /* InvalidGrant */,
@@ -1669,13 +1971,20 @@ async function verifyPreAuthorizedCodeAccessTokenRequest(options) {
1669
1971
  );
1670
1972
  }
1671
1973
  }
1974
+ return { dpop: dpopResult, clientAttestation: clientAttestationResult };
1975
+ }
1976
+ async function verifyAuthorizationCodeAccessTokenRequest(options) {
1672
1977
  if (options.pkce) {
1673
1978
  await verifyAccessTokenRequestPkce(options.pkce, options.callbacks);
1674
1979
  }
1675
- const dpopResult = options.dpop ? await verifyAccessTokenRequestDpop(options.dpop, options.request, options.callbacks) : null;
1676
- return { dpopJwk: dpopResult?.dpopJwk };
1677
- }
1678
- async function verifyAuthorizationCodeAccessTokenRequest(options) {
1980
+ const dpopResult = options.dpop ? await verifyAccessTokenRequestDpop(options.dpop, options.request, options.callbacks) : void 0;
1981
+ const clientAttestationResult = options.clientAttestation ? await verifyAccessTokenRequestClientAttestation(
1982
+ options.clientAttestation,
1983
+ options.authorizationServerMetadata,
1984
+ options.callbacks,
1985
+ dpopResult?.jwkThumbprint,
1986
+ options.now
1987
+ ) : void 0;
1679
1988
  if (options.grant.code !== options.expectedCode) {
1680
1989
  throw new Oauth2ServerErrorResponseError({
1681
1990
  error: "invalid_grant" /* InvalidGrant */,
@@ -1696,11 +2005,55 @@ async function verifyAuthorizationCodeAccessTokenRequest(options) {
1696
2005
  );
1697
2006
  }
1698
2007
  }
1699
- if (options.pkce) {
1700
- await verifyAccessTokenRequestPkce(options.pkce, options.callbacks);
2008
+ return { dpop: dpopResult, clientAttestation: clientAttestationResult };
2009
+ }
2010
+ async function verifyAccessTokenRequestClientAttestation(options, authorizationServerMetadata, callbacks, dpopJwkThumbprint, now) {
2011
+ if (!options.clientAttestationJwt || !options.clientAttestationPopJwt) {
2012
+ if (!options.required && !options.clientAttestationJwt && !options.clientAttestationPopJwt) {
2013
+ return void 0;
2014
+ }
2015
+ throw new Oauth2ServerErrorResponseError({
2016
+ error: "invalid_dpop_proof" /* InvalidDpopProof */,
2017
+ error_description: `Missing required client attestation parameters in access token request. Make sure to provide the '${oauthClientAttestationHeader}' and '${oauthClientAttestationPopHeader}' header values.`
2018
+ });
2019
+ }
2020
+ const verifiedClientAttestation = await verifyClientAttestation({
2021
+ authorizationServer: authorizationServerMetadata.issuer,
2022
+ callbacks,
2023
+ clientAttestationJwt: options.clientAttestationJwt,
2024
+ clientAttestationPopJwt: options.clientAttestationPopJwt,
2025
+ now
2026
+ });
2027
+ if (options.expectedClientId !== verifiedClientAttestation.clientAttestation.payload.sub) {
2028
+ throw new Oauth2ServerErrorResponseError(
2029
+ {
2030
+ error: "invalid_client" /* InvalidClient */,
2031
+ error_description: `The client id '${verifiedClientAttestation.clientAttestation.payload.sub}' in the client attestation does not match the client id for the authorization.`
2032
+ },
2033
+ {
2034
+ status: 401
2035
+ }
2036
+ );
2037
+ }
2038
+ if (options.ensureConfirmationKeyMatchesDpopKey && dpopJwkThumbprint) {
2039
+ const clientAttestationJkt = await calculateJwkThumbprint({
2040
+ hashAlgorithm: "sha-256" /* Sha256 */,
2041
+ hashCallback: callbacks.hash,
2042
+ jwk: verifiedClientAttestation.clientAttestation.payload.cnf.jwk
2043
+ });
2044
+ if (clientAttestationJkt !== dpopJwkThumbprint) {
2045
+ throw new Oauth2ServerErrorResponseError(
2046
+ {
2047
+ error: "invalid_request" /* InvalidRequest */,
2048
+ error_description: "Expected the DPoP JWK thumbprint value to match the JWK thumbprint of the client attestation confirmation JWK. Ensrue both DPoP and client attestation use the same key."
2049
+ },
2050
+ {
2051
+ status: 401
2052
+ }
2053
+ );
2054
+ }
1701
2055
  }
1702
- const dpopResult = options.dpop ? await verifyAccessTokenRequestDpop(options.dpop, options.request, options.callbacks) : null;
1703
- return { dpopJwk: dpopResult?.dpopJwk };
2056
+ return verifiedClientAttestation;
1704
2057
  }
1705
2058
  async function verifyAccessTokenRequestDpop(options, request, callbacks) {
1706
2059
  if (options.required && !options.jwt) {
@@ -1709,26 +2062,18 @@ async function verifyAccessTokenRequestDpop(options, request, callbacks) {
1709
2062
  error_description: "Missing required DPoP proof"
1710
2063
  });
1711
2064
  }
1712
- if (!options.jwt) return null;
1713
- try {
1714
- const { header } = await verifyDpopJwt({
1715
- callbacks,
1716
- dpopJwt: options.jwt,
1717
- request,
1718
- allowedSigningAlgs: options.allowedSigningAlgs
1719
- });
1720
- return {
1721
- dpopJwk: header.jwk
1722
- };
1723
- } catch (error) {
1724
- if (error instanceof Oauth2Error) {
1725
- throw new Oauth2ServerErrorResponseError({
1726
- error: "invalid_dpop_proof" /* InvalidDpopProof */,
1727
- error_description: error.message
1728
- });
1729
- }
1730
- throw error;
1731
- }
2065
+ if (!options.jwt) return void 0;
2066
+ const { header, jwkThumbprint } = await verifyDpopJwt({
2067
+ callbacks,
2068
+ dpopJwt: options.jwt,
2069
+ request,
2070
+ allowedSigningAlgs: options.allowedSigningAlgs,
2071
+ expectedJwkThumbprint: options.expectedJwkThumbprint
2072
+ });
2073
+ return {
2074
+ jwk: header.jwk,
2075
+ jwkThumbprint
2076
+ };
1732
2077
  }
1733
2078
  async function verifyAccessTokenRequestPkce(options, callbacks) {
1734
2079
  if (options.codeChallenge && !options.codeVerifier) {
@@ -1757,69 +2102,69 @@ async function verifyAccessTokenRequestPkce(options, callbacks) {
1757
2102
  }
1758
2103
 
1759
2104
  // src/authorization-challenge/create-authorization-challenge-response.ts
1760
- import { parseWithErrorHandling as parseWithErrorHandling8 } from "@openid4vc/utils";
2105
+ import { parseWithErrorHandling as parseWithErrorHandling10 } from "@openid4vc/utils";
1761
2106
 
1762
2107
  // src/authorization-challenge/z-authorization-challenge.ts
1763
- import { zInteger as zInteger5 } from "@openid4vc/utils";
1764
- import z14 from "zod";
2108
+ import { zInteger as zInteger6 } from "@openid4vc/utils";
2109
+ import z15 from "zod";
1765
2110
 
1766
2111
  // src/authorization-request/z-authorization-request.ts
1767
- import { zHttpsUrl as zHttpsUrl4 } from "@openid4vc/utils";
1768
- import z13 from "zod";
1769
- var zAuthorizationRequest = z13.object({
1770
- response_type: z13.string(),
1771
- client_id: z13.string(),
1772
- issuer_state: z13.optional(z13.string()),
1773
- redirect_uri: z13.string().url().optional(),
1774
- resource: z13.optional(zHttpsUrl4),
1775
- scope: z13.optional(z13.string()),
2112
+ import { zHttpsUrl as zHttpsUrl5 } from "@openid4vc/utils";
2113
+ import z14 from "zod";
2114
+ var zAuthorizationRequest = z14.object({
2115
+ response_type: z14.string(),
2116
+ client_id: z14.string(),
2117
+ issuer_state: z14.optional(z14.string()),
2118
+ redirect_uri: z14.string().url().optional(),
2119
+ resource: z14.optional(zHttpsUrl5),
2120
+ scope: z14.optional(z14.string()),
1776
2121
  // DPoP jwk thumbprint
1777
- dpop_jkt: z13.optional(z13.string()),
1778
- code_challenge: z13.optional(z13.string()),
1779
- code_challenge_method: z13.optional(z13.string())
2122
+ dpop_jkt: z14.optional(z14.string().base64url()),
2123
+ code_challenge: z14.optional(z14.string()),
2124
+ code_challenge_method: z14.optional(z14.string())
1780
2125
  }).passthrough();
1781
- var zPushedAuthorizationRequest = z13.object({
1782
- request_uri: z13.string(),
1783
- client_id: z13.string()
2126
+ var zPushedAuthorizationRequest = z14.object({
2127
+ request_uri: z14.string(),
2128
+ client_id: z14.string()
1784
2129
  }).passthrough();
1785
- var zPushedAuthorizationResponse = z13.object({
1786
- request_uri: z13.string(),
1787
- expires_in: z13.number().int()
2130
+ var zPushedAuthorizationResponse = z14.object({
2131
+ request_uri: z14.string(),
2132
+ expires_in: z14.number().int()
1788
2133
  }).passthrough();
1789
2134
 
1790
2135
  // src/authorization-challenge/z-authorization-challenge.ts
1791
- var zAuthorizationChallengeRequest = z14.object({
2136
+ var zAuthorizationChallengeRequest = z15.object({
1792
2137
  // authorization challenge request can include same parameters as an authorization request
1793
2138
  // except for response_type (always `code`), and `client_id` is optional (becase
1794
2139
  // it's possible to do client authentication using different methods)
1795
2140
  ...zAuthorizationRequest.omit({ response_type: true, client_id: true }).shape,
1796
- client_id: z14.optional(zAuthorizationRequest.shape.client_id),
1797
- auth_session: z14.optional(z14.string()),
2141
+ client_id: z15.optional(zAuthorizationRequest.shape.client_id),
2142
+ auth_session: z15.optional(z15.string()),
1798
2143
  // DRAFT presentation during issuance
1799
- presentation_during_issuance_session: z14.optional(z14.string())
2144
+ presentation_during_issuance_session: z15.optional(z15.string())
1800
2145
  }).passthrough();
1801
- var zAuthorizationChallengeResponse = z14.object({
1802
- authorization_code: z14.string()
2146
+ var zAuthorizationChallengeResponse = z15.object({
2147
+ authorization_code: z15.string()
1803
2148
  }).passthrough();
1804
- var zAuthorizationChallengeErrorResponse = z14.object({
2149
+ var zAuthorizationChallengeErrorResponse = z15.object({
1805
2150
  ...zOauth2ErrorResponse.shape,
1806
- auth_session: z14.optional(z14.string()),
1807
- request_uri: z14.optional(z14.string()),
1808
- expires_in: z14.optional(zInteger5),
2151
+ auth_session: z15.optional(z15.string()),
2152
+ request_uri: z15.optional(z15.string()),
2153
+ expires_in: z15.optional(zInteger6),
1809
2154
  // DRAFT: presentation during issuance
1810
- presentation: z14.optional(z14.string())
2155
+ presentation: z15.optional(z15.string())
1811
2156
  }).passthrough();
1812
2157
 
1813
2158
  // src/authorization-challenge/create-authorization-challenge-response.ts
1814
2159
  function createAuthorizationChallengeResponse(options) {
1815
- const authorizationChallengeResponse = parseWithErrorHandling8(zAuthorizationChallengeResponse, {
2160
+ const authorizationChallengeResponse = parseWithErrorHandling10(zAuthorizationChallengeResponse, {
1816
2161
  ...options.additionalPayload,
1817
2162
  authorization_code: options.authorizationCode
1818
2163
  });
1819
2164
  return { authorizationChallengeResponse };
1820
2165
  }
1821
2166
  function createAuthorizationChallengeErrorResponse(options) {
1822
- const authorizationChallengeErrorResponse = parseWithErrorHandling8(zAuthorizationChallengeErrorResponse, {
2167
+ const authorizationChallengeErrorResponse = parseWithErrorHandling10(zAuthorizationChallengeErrorResponse, {
1823
2168
  ...options.additionalPayload,
1824
2169
  // General FiPA
1825
2170
  error: options.error,
@@ -1835,203 +2180,220 @@ function createAuthorizationChallengeErrorResponse(options) {
1835
2180
  }
1836
2181
 
1837
2182
  // src/authorization-challenge/parse-authorization-challenge-request.ts
1838
- import { parseWithErrorHandling as parseWithErrorHandling9 } from "@openid4vc/utils";
2183
+ import { formatZodError as formatZodError3 } from "@openid4vc/utils";
2184
+
2185
+ // src/authorization-request/parse-authorization-request.ts
2186
+ function parseAuthorizationRequest(options) {
2187
+ const extractedDpopJwt = extractDpopJwtFromHeaders(options.request.headers);
2188
+ if (!extractedDpopJwt.valid) {
2189
+ throw new Oauth2ServerErrorResponseError({
2190
+ error: "invalid_dpop_proof" /* InvalidDpopProof */,
2191
+ error_description: `Request contains a 'DPoP' header, but the value is not a valid DPoP jwt`
2192
+ });
2193
+ }
2194
+ const extractedClientAttestationJwts = extractClientAttestationJwtsFromHeaders(options.request.headers);
2195
+ if (!extractedClientAttestationJwts.valid) {
2196
+ throw new Oauth2ServerErrorResponseError({
2197
+ error: "invalid_client" /* InvalidClient */,
2198
+ error_description: "Request contains client attestation header, but the values are not valid client attestation and client attestation PoP header."
2199
+ });
2200
+ }
2201
+ return {
2202
+ dpop: extractedDpopJwt.dpopJwt ? {
2203
+ jwt: extractedDpopJwt.dpopJwt,
2204
+ jwkThumbprint: options.authorizationRequest.dpop_jkt
2205
+ } : (
2206
+ // Basically the same as above, but with correct TS type hinting
2207
+ options.authorizationRequest.dpop_jkt ? {
2208
+ jwt: extractedDpopJwt.dpopJwt,
2209
+ jwkThumbprint: options.authorizationRequest.dpop_jkt
2210
+ } : void 0
2211
+ ),
2212
+ clientAttestation: extractedClientAttestationJwts.clientAttestationHeader ? {
2213
+ clientAttestationJwt: extractedClientAttestationJwts.clientAttestationHeader,
2214
+ clientAttestationPopJwt: extractedClientAttestationJwts.clientAttestationPopHeader
2215
+ } : void 0
2216
+ };
2217
+ }
2218
+
2219
+ // src/authorization-challenge/parse-authorization-challenge-request.ts
1839
2220
  function parseAuthorizationChallengeRequest(options) {
1840
- const authorizationChallengeRequest = parseWithErrorHandling9(
1841
- zAuthorizationChallengeRequest,
2221
+ const parsedAuthorizationChallengeRequest = zAuthorizationChallengeRequest.safeParse(
1842
2222
  options.authorizationChallengeRequest
1843
2223
  );
1844
- return { authorizationChallengeRequest };
2224
+ if (!parsedAuthorizationChallengeRequest.success) {
2225
+ throw new Oauth2ServerErrorResponseError({
2226
+ error: "invalid_request" /* InvalidRequest */,
2227
+ error_description: `Error occured during validation of authorization challenge request.
2228
+ ${formatZodError3(parsedAuthorizationChallengeRequest.error)}`
2229
+ });
2230
+ }
2231
+ const authorizationChallengeRequest = parsedAuthorizationChallengeRequest.data;
2232
+ const { clientAttestation, dpop } = parseAuthorizationRequest({
2233
+ authorizationRequest: authorizationChallengeRequest,
2234
+ request: options.request
2235
+ });
2236
+ return {
2237
+ authorizationChallengeRequest: parsedAuthorizationChallengeRequest.data,
2238
+ dpop,
2239
+ clientAttestation
2240
+ };
1845
2241
  }
1846
2242
 
1847
- // src/client-attestation/clent-attestation.ts
1848
- import { dateToSeconds as dateToSeconds4, parseWithErrorHandling as parseWithErrorHandling10 } from "@openid4vc/utils";
1849
-
1850
- // src/client-attestation/z-client-attestation.ts
1851
- import { zHttpsUrl as zHttpsUrl5, zInteger as zInteger6 } from "@openid4vc/utils";
1852
- import z15 from "zod";
1853
- var zOauthClientAttestationHeader = z15.literal("OAuth-Client-Attestation");
1854
- var oauthClientAttestationHeader = zOauthClientAttestationHeader.value;
1855
- var zClientAttestationJwtPayload = z15.object({
1856
- ...zJwtPayload.shape,
1857
- iss: z15.string(),
1858
- sub: z15.string(),
1859
- exp: zInteger6,
1860
- cnf: z15.object({
1861
- jwk: zJwk,
1862
- key_type: z15.optional(
1863
- z15.union([
1864
- z15.enum(["software", "hardware", "tee", "secure_enclave", "strong_box", "secure_element", "hsm"]),
1865
- z15.string()
1866
- ])
1867
- ),
1868
- user_authentication: z15.optional(
1869
- z15.union([
1870
- z15.enum(["system_biometry", "system_pin", "internal_biometry", "internal_pin", "secure_element_pin"]),
1871
- z15.string()
1872
- ])
1873
- )
1874
- }).passthrough(),
1875
- aal: z15.optional(z15.string())
1876
- }).passthrough();
1877
- var zClientAttestationJwtHeader = z15.object({
1878
- ...zJwtHeader.shape,
1879
- typ: z15.literal("oauth-client-attestation+jwt")
1880
- }).passthrough();
1881
- var zOauthClientAttestationPopHeader = z15.literal("OAuth-Client-Attestation-PoP");
1882
- var oauthClientAttestationPopHeader = zOauthClientAttestationPopHeader.value;
1883
- var zClientAttestationPopJwtPayload = z15.object({
1884
- ...zJwtPayload.shape,
1885
- iss: z15.string(),
1886
- exp: zInteger6,
1887
- aud: zHttpsUrl5,
1888
- jti: z15.string(),
1889
- nonce: z15.optional(z15.string())
1890
- }).passthrough();
1891
- var zClientAttestationPopJwtHeader = z15.object({
1892
- ...zJwtHeader.shape,
1893
- typ: z15.literal("oauth-client-attestation-pop+jwt")
1894
- }).passthrough();
1895
-
1896
- // src/client-attestation/clent-attestation.ts
1897
- async function verifyClientAttestationJwt(options) {
1898
- const { header, payload } = decodeJwt({
1899
- jwt: options.clientAttestationJwt,
1900
- headerSchema: zClientAttestationJwtHeader,
1901
- payloadSchema: zClientAttestationJwtPayload
1902
- });
1903
- const { signer } = await verifyJwt({
1904
- signer: jwtSignerFromJwt({ header, payload }),
1905
- now: options.now,
1906
- header,
1907
- payload,
1908
- compact: options.clientAttestationJwt,
1909
- verifyJwtCallback: options.callbacks.verifyJwt,
1910
- errorMessage: "client attestation jwt verification failed"
1911
- });
2243
+ // src/authorization-request/verify-authorization-request.ts
2244
+ async function verifyAuthorizationRequest(options) {
2245
+ const dpopResult = options.dpop ? await verifyAuthorizationRequestDpop(options.dpop, options.request, options.callbacks, options.now) : void 0;
2246
+ const clientAttestationResult = options.clientAttestation ? await verifyAuthorizationRequestClientAttestation(
2247
+ options.clientAttestation,
2248
+ options.authorizationServerMetadata,
2249
+ options.callbacks,
2250
+ dpopResult?.jwkThumbprint,
2251
+ options.now,
2252
+ options.authorizationRequest.client_id
2253
+ ) : void 0;
1912
2254
  return {
1913
- header,
1914
- payload,
1915
- signer
2255
+ dpop: dpopResult?.jwkThumbprint ? {
2256
+ jwkThumbprint: dpopResult.jwkThumbprint,
2257
+ jwk: dpopResult.jwk
2258
+ } : void 0,
2259
+ clientAttestation: clientAttestationResult
1916
2260
  };
1917
2261
  }
1918
- async function createClientAttestationJwt(options) {
1919
- const header = parseWithErrorHandling10(zClientAttestationJwtHeader, {
1920
- typ: "oauth-client-attestation+jwt",
1921
- ...jwtHeaderFromJwtSigner(options.signer)
1922
- });
1923
- const payload = parseWithErrorHandling10(zClientAttestationJwtPayload, {
1924
- iss: options.issuer,
1925
- iat: dateToSeconds4(options.issuedAt),
1926
- exp: dateToSeconds4(options.expiresAt),
1927
- sub: options.clientId,
1928
- cnf: options.confirmation,
1929
- ...options.additionalPayload
1930
- });
1931
- const { jwt } = await options.callbacks.signJwt(options.signer, {
1932
- header,
1933
- payload
2262
+ async function verifyAuthorizationRequestClientAttestation(options, authorizationServerMetadata, callbacks, dpopJwkThumbprint, now, requestClientId) {
2263
+ if (!options.clientAttestationJwt || !options.clientAttestationPopJwt) {
2264
+ if (!options.required && !options.clientAttestationJwt && !options.clientAttestationPopJwt) {
2265
+ return void 0;
2266
+ }
2267
+ throw new Oauth2ServerErrorResponseError({
2268
+ error: "invalid_dpop_proof" /* InvalidDpopProof */,
2269
+ error_description: `Missing required client attestation parameters in pushed authorization request. Make sure to provide the '${oauthClientAttestationHeader}' and '${oauthClientAttestationPopHeader}' header values.`
2270
+ });
2271
+ }
2272
+ const verifiedClientAttestation = await verifyClientAttestation({
2273
+ authorizationServer: authorizationServerMetadata.issuer,
2274
+ callbacks,
2275
+ clientAttestationJwt: options.clientAttestationJwt,
2276
+ clientAttestationPopJwt: options.clientAttestationPopJwt,
2277
+ now
1934
2278
  });
1935
- return jwt;
2279
+ if (requestClientId && requestClientId !== verifiedClientAttestation.clientAttestation.payload.sub) {
2280
+ throw new Oauth2ServerErrorResponseError(
2281
+ {
2282
+ error: "invalid_client" /* InvalidClient */,
2283
+ error_description: `The client_id '${requestClientId}' in the request does not match the client id '${verifiedClientAttestation.clientAttestation.payload.sub}' in the client attestation`
2284
+ },
2285
+ {
2286
+ status: 401
2287
+ }
2288
+ );
2289
+ }
2290
+ if (options.ensureConfirmationKeyMatchesDpopKey && dpopJwkThumbprint) {
2291
+ const clientAttestationJkt = await calculateJwkThumbprint({
2292
+ hashAlgorithm: "sha-256" /* Sha256 */,
2293
+ hashCallback: callbacks.hash,
2294
+ jwk: verifiedClientAttestation.clientAttestation.payload.cnf.jwk
2295
+ });
2296
+ if (clientAttestationJkt !== dpopJwkThumbprint) {
2297
+ throw new Oauth2ServerErrorResponseError(
2298
+ {
2299
+ error: "invalid_request" /* InvalidRequest */,
2300
+ error_description: "Expected the DPoP JWK thumbprint value to match the JWK thumbprint of the client attestation confirmation JWK. Ensrue both DPoP and client attestation use the same key."
2301
+ },
2302
+ {
2303
+ status: 401
2304
+ }
2305
+ );
2306
+ }
2307
+ }
2308
+ return verifiedClientAttestation;
1936
2309
  }
1937
- function extractClientAttestationJwtsFromHeaders(headers) {
1938
- const clientAttestationHeader = headers.get(oauthClientAttestationHeader);
1939
- const clientAttestationPopHeader = headers.get(oauthClientAttestationPopHeader);
1940
- if (!clientAttestationHeader || clientAttestationHeader.includes(",")) {
1941
- throw new Oauth2Error(`Missing or invalid '${oauthClientAttestationHeader}' header.`);
2310
+ async function verifyAuthorizationRequestDpop(options, request, callbacks, now) {
2311
+ if (options.required && !options.jwt && !options.jwkThumbprint) {
2312
+ throw new Oauth2ServerErrorResponseError({
2313
+ error: "invalid_dpop_proof" /* InvalidDpopProof */,
2314
+ error_description: `Missing required DPoP parameters in authorization request. Either DPoP header or 'dpop_jkt' is required.`
2315
+ });
1942
2316
  }
1943
- if (!clientAttestationPopHeader || clientAttestationPopHeader.includes(",")) {
1944
- throw new Oauth2Error(`Missing or invalid '${oauthClientAttestationPopHeader}' header.`);
2317
+ const verifyDpopResult = options.jwt ? await verifyDpopJwt({
2318
+ callbacks,
2319
+ dpopJwt: options.jwt,
2320
+ request,
2321
+ allowedSigningAlgs: options.allowedSigningAlgs,
2322
+ now
2323
+ }) : void 0;
2324
+ if (options.jwkThumbprint && verifyDpopResult && options.jwkThumbprint !== verifyDpopResult.jwkThumbprint) {
2325
+ throw new Oauth2ServerErrorResponseError({
2326
+ error: "invalid_dpop_proof" /* InvalidDpopProof */,
2327
+ error_description: `DPoP jwk thumbprint does not match with 'dpop_jkt' provided in authorization request`
2328
+ });
1945
2329
  }
1946
2330
  return {
1947
- clientAttestationPopHeader,
1948
- clientAttestationHeader
2331
+ jwk: verifyDpopResult?.header.jwk,
2332
+ jwkThumbprint: verifyDpopResult?.jwkThumbprint ?? options.jwkThumbprint
1949
2333
  };
1950
2334
  }
1951
2335
 
1952
- // src/client-attestation/client-attestation-pop.ts
1953
- import { addSecondsToDate as addSecondsToDate2, dateToSeconds as dateToSeconds5, encodeToBase64Url as encodeToBase64Url6, parseWithErrorHandling as parseWithErrorHandling11 } from "@openid4vc/utils";
1954
- async function createClientAttestationForRequest(options) {
1955
- const clientAttestationPopJwt = await createClientAttestationPopJwt({
1956
- authorizationServer: options.authorizationServer,
1957
- clientAttestation: options.clientAttestation.jwt,
1958
- callbacks: options.callbacks,
1959
- expiresAt: options.clientAttestation.expiresAt,
1960
- signer: options.clientAttestation.signer,
1961
- nonce: options.clientAttestation.nonce
2336
+ // src/authorization-challenge/verify-authorization-challenge-request.ts
2337
+ async function verifyAuthorizationChallengeRequest(options) {
2338
+ const { clientAttestation, dpop } = await verifyAuthorizationRequest({
2339
+ ...options,
2340
+ authorizationRequest: options.authorizationChallengeRequest
1962
2341
  });
1963
2342
  return {
1964
- headers: {
1965
- [oauthClientAttestationHeader]: options.clientAttestation.jwt,
1966
- [oauthClientAttestationPopHeader]: clientAttestationPopJwt
1967
- },
1968
- body: options.clientAttestation.includeLegacyDraft2ClientAssertion ? {
1969
- client_assertion: `${options.clientAttestation.jwt}~${clientAttestationPopJwt}`,
1970
- client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-client-attestation"
1971
- } : void 0
2343
+ dpop,
2344
+ clientAttestation
1972
2345
  };
1973
2346
  }
1974
- async function verifyClientAttestationPopJwt(options) {
1975
- const { header, payload } = decodeJwt({
1976
- jwt: options.clientAttestationPopJwt,
1977
- headerSchema: zClientAttestationPopJwtHeader,
1978
- payloadSchema: zClientAttestationPopJwtPayload
2347
+
2348
+ // src/authorization-request/create-pushed-authorization-response.ts
2349
+ import { parseWithErrorHandling as parseWithErrorHandling11 } from "@openid4vc/utils";
2350
+ function createPushedAuthorizationResponse(options) {
2351
+ const pushedAuthorizationResponse = parseWithErrorHandling11(zPushedAuthorizationResponse, {
2352
+ ...options.additionalPayload,
2353
+ expires_in: options.expiresInSeconds,
2354
+ request_uri: options.requestUri
1979
2355
  });
1980
- if (payload.iss !== options.clientAttestation.payload.sub) {
1981
- throw new Oauth2Error(
1982
- `Client Attestation Pop jwt contains 'iss' (client_id) value '${payload.iss}', but expected 'sub' value from client attestation '${options.clientAttestation.payload.sub}'`
1983
- );
1984
- }
1985
- if (payload.aud !== options.authorizationServer) {
1986
- throw new Oauth2Error(
1987
- `Client Attestation Pop jwt contains 'aud' value '${payload.aud}', but expected authorization server identifier '${options.authorizationServer}'`
1988
- );
2356
+ return { pushedAuthorizationResponse };
2357
+ }
2358
+ function createPushedAuthorizationErrorResponse(options) {
2359
+ const pushedAuthorizationErrorResponse = parseWithErrorHandling11(zAccessTokenErrorResponse, {
2360
+ ...options.additionalPayload,
2361
+ error: options.error,
2362
+ error_description: options.errorDescription
2363
+ });
2364
+ return pushedAuthorizationErrorResponse;
2365
+ }
2366
+
2367
+ // src/authorization-request/parse-pushed-authorization-request.ts
2368
+ import { formatZodError as formatZodError4 } from "@openid4vc/utils";
2369
+ function parsePushedAuthorizationRequest(options) {
2370
+ const parsedAuthorizationRequest = zAuthorizationRequest.safeParse(options.authorizationRequest);
2371
+ if (!parsedAuthorizationRequest.success) {
2372
+ throw new Oauth2ServerErrorResponseError({
2373
+ error: "invalid_request" /* InvalidRequest */,
2374
+ error_description: `Error occured during validation of pushed authorization request.
2375
+ ${formatZodError4(parsedAuthorizationRequest.error)}`
2376
+ });
1989
2377
  }
1990
- const { signer } = await verifyJwt({
1991
- signer: {
1992
- alg: header.alg,
1993
- method: "jwk",
1994
- publicJwk: options.clientAttestation.payload.cnf.jwk
1995
- },
1996
- now: options.now,
1997
- header,
1998
- expectedNonce: options.expectedNonce,
1999
- payload,
2000
- compact: options.clientAttestationPopJwt,
2001
- verifyJwtCallback: options.callbacks.verifyJwt,
2002
- errorMessage: "client attestation pop jwt verification failed"
2378
+ const authorizationRequest = parsedAuthorizationRequest.data;
2379
+ const { clientAttestation, dpop } = parseAuthorizationRequest({
2380
+ authorizationRequest,
2381
+ request: options.request
2003
2382
  });
2004
2383
  return {
2005
- header,
2006
- payload,
2007
- signer
2384
+ authorizationRequest,
2385
+ dpop,
2386
+ clientAttestation
2008
2387
  };
2009
2388
  }
2010
- async function createClientAttestationPopJwt(options) {
2011
- const header = parseWithErrorHandling11(zClientAttestationPopJwtHeader, {
2012
- typ: "oauth-client-attestation-pop+jwt",
2013
- alg: options.signer.alg
2014
- });
2015
- const clientAttestation = decodeJwt({
2016
- jwt: options.clientAttestation,
2017
- headerSchema: zClientAttestationJwtHeader,
2018
- payloadSchema: zClientAttestationJwtPayload
2019
- });
2020
- const expiresAt = options.expiresAt ?? addSecondsToDate2(options.issuedAt ?? /* @__PURE__ */ new Date(), 1 * 60);
2021
- const payload = parseWithErrorHandling11(zClientAttestationPopJwtPayload, {
2022
- aud: options.authorizationServer,
2023
- iss: clientAttestation.payload.sub,
2024
- iat: dateToSeconds5(options.issuedAt),
2025
- exp: dateToSeconds5(expiresAt),
2026
- jti: encodeToBase64Url6(await options.callbacks.generateRandom(32)),
2027
- nonce: options.nonce,
2028
- ...options.additionalPayload
2029
- });
2030
- const { jwt } = await options.callbacks.signJwt(options.signer, {
2031
- header,
2032
- payload
2033
- });
2034
- return jwt;
2389
+
2390
+ // src/authorization-request/verify-pushed-authorization-request.ts
2391
+ async function verifyPushedAuthorizationRequest(options) {
2392
+ const { clientAttestation, dpop } = await verifyAuthorizationRequest(options);
2393
+ return {
2394
+ dpop,
2395
+ clientAttestation
2396
+ };
2035
2397
  }
2036
2398
 
2037
2399
  // src/Oauth2AuthorizationServer.ts
@@ -2085,7 +2447,7 @@ var Oauth2AuthorizationServer = class {
2085
2447
  scope: options.scope,
2086
2448
  clientId: options.clientId,
2087
2449
  signer: options.signer,
2088
- dpopJwk: options.dpopJwk,
2450
+ dpop: options.dpop,
2089
2451
  now: options.now,
2090
2452
  additionalPayload: options.additionalAccessTokenPayload
2091
2453
  });
@@ -2093,23 +2455,47 @@ var Oauth2AuthorizationServer = class {
2093
2455
  accessToken,
2094
2456
  callbacks: this.options.callbacks,
2095
2457
  expiresInSeconds: options.expiresInSeconds,
2096
- tokenType: options.dpopJwk ? "DPoP" : "Bearer",
2458
+ tokenType: options.dpop ? "DPoP" : "Bearer",
2097
2459
  cNonce: options.cNonce,
2098
2460
  cNonceExpiresIn: options.cNonceExpiresIn,
2099
2461
  additionalPayload: options.additionalAccessTokenResponsePayload
2100
2462
  });
2101
2463
  }
2464
+ /**
2465
+ * Parse a pushed authorization request
2466
+ */
2467
+ parsePushedAuthorizationRequest(options) {
2468
+ return parsePushedAuthorizationRequest(options);
2469
+ }
2470
+ verifyPushedAuthorizationRequest(options) {
2471
+ return verifyPushedAuthorizationRequest({
2472
+ ...options,
2473
+ callbacks: this.options.callbacks
2474
+ });
2475
+ }
2476
+ createPushedAuthorizationResponse(options) {
2477
+ return createPushedAuthorizationResponse(options);
2478
+ }
2479
+ createPushedAuthorizationErrorResponse(options) {
2480
+ return createPushedAuthorizationErrorResponse(options);
2481
+ }
2102
2482
  /**
2103
2483
  * Parse an authorization challenge request
2104
2484
  */
2105
2485
  parseAuthorizationChallengeRequest(options) {
2106
2486
  return parseAuthorizationChallengeRequest(options);
2107
2487
  }
2488
+ verifyAuthorizationChallengeRequest(options) {
2489
+ return verifyAuthorizationChallengeRequest({
2490
+ ...options,
2491
+ callbacks: this.options.callbacks
2492
+ });
2493
+ }
2108
2494
  createAuthorizationChallengeResponse(options) {
2109
2495
  return createAuthorizationChallengeResponse(options);
2110
2496
  }
2111
2497
  /**
2112
- * Create an authorization challenge error response indicating presentation of credenitals
2498
+ * Create an authorization challenge error response indicating presentation of credentials
2113
2499
  * using OpenID4VP is required before authorization can be granted.
2114
2500
  *
2115
2501
  * The `presentation` parameter should be an OpenID4VP authorization request url.
@@ -2127,25 +2513,17 @@ var Oauth2AuthorizationServer = class {
2127
2513
  createAuthorizationChallengeErrorResponse(options) {
2128
2514
  return createAuthorizationChallengeErrorResponse(options);
2129
2515
  }
2130
- async verifyClientAttestation({
2131
- authorizationServer,
2132
- headers
2133
- }) {
2134
- const { clientAttestationHeader, clientAttestationPopHeader } = extractClientAttestationJwtsFromHeaders(headers);
2135
- const clientAttestation = await verifyClientAttestationJwt({
2136
- callbacks: this.options.callbacks,
2137
- clientAttestationJwt: clientAttestationHeader
2516
+ async verifyDpopJwt(options) {
2517
+ return verifyDpopJwt({
2518
+ ...options,
2519
+ callbacks: this.options.callbacks
2138
2520
  });
2139
- const clientAttestationPop = await verifyClientAttestationPopJwt({
2140
- callbacks: this.options.callbacks,
2141
- authorizationServer,
2142
- clientAttestation,
2143
- clientAttestationPopJwt: clientAttestationPopHeader
2521
+ }
2522
+ async verifyClientAttestation(options) {
2523
+ return verifyClientAttestation({
2524
+ ...options,
2525
+ callbacks: this.options.callbacks
2144
2526
  });
2145
- return {
2146
- clientAttestation,
2147
- clientAttestationPop
2148
- };
2149
2527
  }
2150
2528
  };
2151
2529
 
@@ -2153,7 +2531,7 @@ var Oauth2AuthorizationServer = class {
2153
2531
  import { objectToQueryParams as objectToQueryParams5 } from "@openid4vc/utils";
2154
2532
 
2155
2533
  // src/access-token/retrieve-access-token.ts
2156
- import { ContentType as ContentType4, createZodFetcher as createZodFetcher4, objectToQueryParams as objectToQueryParams2, parseWithErrorHandling as parseWithErrorHandling13 } from "@openid4vc/utils";
2534
+ import { ContentType as ContentType4, Headers as Headers2, createZodFetcher as createZodFetcher4, objectToQueryParams as objectToQueryParams2, parseWithErrorHandling as parseWithErrorHandling13 } from "@openid4vc/utils";
2157
2535
  import { InvalidFetchResponseError as InvalidFetchResponseError4 } from "@openid4vc/utils";
2158
2536
  async function retrievePreAuthorizedCodeAccessToken(options) {
2159
2537
  const request = {
@@ -2168,8 +2546,7 @@ async function retrievePreAuthorizedCodeAccessToken(options) {
2168
2546
  request,
2169
2547
  dpop: options.dpop,
2170
2548
  callbacks: options.callbacks,
2171
- resource: options.resource,
2172
- clientAttestation: options.clientAttestation
2549
+ resource: options.resource
2173
2550
  });
2174
2551
  }
2175
2552
  async function retrieveAuthorizationCodeAccessToken(options) {
@@ -2186,8 +2563,7 @@ async function retrieveAuthorizationCodeAccessToken(options) {
2186
2563
  request,
2187
2564
  dpop: options.dpop,
2188
2565
  resource: options.resource,
2189
- callbacks: options.callbacks,
2190
- clientAttestation: options.clientAttestation
2566
+ callbacks: options.callbacks
2191
2567
  });
2192
2568
  }
2193
2569
  async function retrieveRefreshTokenAccessToken(options) {
@@ -2202,8 +2578,7 @@ async function retrieveRefreshTokenAccessToken(options) {
2202
2578
  request,
2203
2579
  dpop: options.dpop,
2204
2580
  callbacks: options.callbacks,
2205
- resource: options.resource,
2206
- clientAttestation: options.clientAttestation
2581
+ resource: options.resource
2207
2582
  });
2208
2583
  }
2209
2584
  async function retrieveAccessToken(options) {
@@ -2216,11 +2591,6 @@ async function retrieveAccessToken(options) {
2216
2591
  if (accessTokenRequest.tx_code) {
2217
2592
  accessTokenRequest.user_pin = accessTokenRequest.tx_code;
2218
2593
  }
2219
- const clientAttestation = options.clientAttestation ? await createClientAttestationForRequest({
2220
- authorizationServer: options.authorizationServerMetadata.issuer,
2221
- clientAttestation: options.clientAttestation,
2222
- callbacks: options.callbacks
2223
- }) : void 0;
2224
2594
  return await authorizationServerRequestWithDpopRetry({
2225
2595
  dpop: options.dpop,
2226
2596
  request: async (dpop) => {
@@ -2233,22 +2603,26 @@ async function retrieveAccessToken(options) {
2233
2603
  callbacks: options.callbacks,
2234
2604
  nonce: dpop.nonce
2235
2605
  }) : void 0;
2236
- const requestQueryParams = objectToQueryParams2({
2237
- ...accessTokenRequest,
2238
- ...clientAttestation?.body
2606
+ const headers = new Headers2({
2607
+ "Content-Type": ContentType4.XWwwFormUrlencoded,
2608
+ ...dpopHeaders
2609
+ });
2610
+ await options.callbacks.clientAuthentication({
2611
+ url: options.authorizationServerMetadata.token_endpoint,
2612
+ method: "POST",
2613
+ authorizationServerMetadata: options.authorizationServerMetadata,
2614
+ body: accessTokenRequest,
2615
+ contentType: ContentType4.XWwwFormUrlencoded,
2616
+ headers
2239
2617
  });
2240
2618
  const { response, result } = await fetchWithZod(
2241
2619
  zAccessTokenResponse,
2242
2620
  ContentType4.Json,
2243
2621
  options.authorizationServerMetadata.token_endpoint,
2244
2622
  {
2245
- body: requestQueryParams.toString(),
2623
+ body: objectToQueryParams2(accessTokenRequest).toString(),
2246
2624
  method: "POST",
2247
- headers: {
2248
- "Content-Type": ContentType4.XWwwFormUrlencoded,
2249
- ...clientAttestation?.headers,
2250
- ...dpopHeaders
2251
- }
2625
+ headers
2252
2626
  }
2253
2627
  );
2254
2628
  if (!response.ok || !result) {
@@ -2286,6 +2660,7 @@ async function retrieveAccessToken(options) {
2286
2660
  // src/authorization-challenge/send-authorization-challenge.ts
2287
2661
  import {
2288
2662
  ContentType as ContentType5,
2663
+ Headers as Headers3,
2289
2664
  ValidationError as ValidationError3,
2290
2665
  createZodFetcher as createZodFetcher5,
2291
2666
  objectToQueryParams as objectToQueryParams3,
@@ -2306,21 +2681,14 @@ async function sendAuthorizationChallengeRequest(options) {
2306
2681
  callbacks: options.callbacks,
2307
2682
  codeVerifier: options.pkceCodeVerifier
2308
2683
  }) : void 0;
2309
- const clientAttestation = options.clientAttestation ? await createClientAttestationForRequest({
2310
- authorizationServer: options.authorizationServerMetadata.issuer,
2311
- clientAttestation: options.clientAttestation,
2312
- callbacks: options.callbacks
2313
- }) : void 0;
2314
2684
  const authorizationChallengeRequest = parseWithErrorHandling14(zAuthorizationChallengeRequest, {
2315
2685
  ...options.additionalRequestPayload,
2316
2686
  auth_session: options.authSession,
2317
- client_id: options.clientId,
2318
2687
  scope: options.scope,
2319
2688
  resource: options.resource,
2320
2689
  code_challenge: pkce?.codeChallenge,
2321
2690
  code_challenge_method: pkce?.codeChallengeMethod,
2322
- presentation_during_issuance_session: options.presentationDuringIssuanceSession,
2323
- ...clientAttestation?.body
2691
+ presentation_during_issuance_session: options.presentationDuringIssuanceSession
2324
2692
  });
2325
2693
  return authorizationServerRequestWithDpopRetry({
2326
2694
  dpop: options.dpop,
@@ -2334,6 +2702,18 @@ async function sendAuthorizationChallengeRequest(options) {
2334
2702
  callbacks: options.callbacks,
2335
2703
  nonce: dpop.nonce
2336
2704
  }) : void 0;
2705
+ const headers = new Headers3({
2706
+ ...dpopHeaders,
2707
+ "Content-Type": ContentType5.XWwwFormUrlencoded
2708
+ });
2709
+ await options.callbacks.clientAuthentication({
2710
+ url: authorizationChallengeEndpoint,
2711
+ method: "POST",
2712
+ authorizationServerMetadata: options.authorizationServerMetadata,
2713
+ body: authorizationChallengeRequest,
2714
+ contentType: ContentType5.XWwwFormUrlencoded,
2715
+ headers
2716
+ });
2337
2717
  const { response, result } = await fetchWithZod(
2338
2718
  zAuthorizationChallengeResponse,
2339
2719
  ContentType5.Json,
@@ -2341,11 +2721,7 @@ async function sendAuthorizationChallengeRequest(options) {
2341
2721
  {
2342
2722
  method: "POST",
2343
2723
  body: objectToQueryParams3(authorizationChallengeRequest).toString(),
2344
- headers: {
2345
- ...clientAttestation?.headers,
2346
- ...dpopHeaders,
2347
- "Content-Type": ContentType5.XWwwFormUrlencoded
2348
- }
2724
+ headers
2349
2725
  }
2350
2726
  );
2351
2727
  if (!response.ok || !result) {
@@ -2382,7 +2758,7 @@ async function sendAuthorizationChallengeRequest(options) {
2382
2758
  }
2383
2759
 
2384
2760
  // src/authorization-request/create-authorization-request.ts
2385
- import { ContentType as ContentType6, createZodFetcher as createZodFetcher6, objectToQueryParams as objectToQueryParams4 } from "@openid4vc/utils";
2761
+ import { ContentType as ContentType6, Headers as Headers4, createZodFetcher as createZodFetcher6, objectToQueryParams as objectToQueryParams4 } from "@openid4vc/utils";
2386
2762
  import { InvalidFetchResponseError as InvalidFetchResponseError6 } from "@openid4vc/utils";
2387
2763
  async function createAuthorizationRequestUrl(options) {
2388
2764
  const authorizationServerMetadata = options.authorizationServerMetadata;
@@ -2415,11 +2791,6 @@ async function createAuthorizationRequestUrl(options) {
2415
2791
  `Authorization server '${authorizationServerMetadata.issuer}' indicated that pushed authorization requests are required, but the 'pushed_authorization_request_endpoint' is missing in the authorization server metadata.`
2416
2792
  );
2417
2793
  }
2418
- const clientAttestation = options.clientAttestation ? await createClientAttestationForRequest({
2419
- authorizationServer: options.authorizationServerMetadata.issuer,
2420
- clientAttestation: options.clientAttestation,
2421
- callbacks: options.callbacks
2422
- }) : void 0;
2423
2794
  const { pushedAuthorizationResponse, dpopNonce } = await authorizationServerRequestWithDpopRetry({
2424
2795
  dpop: options.dpop,
2425
2796
  request: async (dpop2) => {
@@ -2433,16 +2804,11 @@ async function createAuthorizationRequestUrl(options) {
2433
2804
  nonce: dpop2.nonce
2434
2805
  }) : void 0;
2435
2806
  return await pushAuthorizationRequest({
2436
- authorizationRequest: {
2437
- ...authorizationRequest,
2438
- ...clientAttestation?.headers
2439
- },
2807
+ authorizationServerMetadata,
2808
+ authorizationRequest,
2440
2809
  pushedAuthorizationRequestEndpoint,
2441
- fetch: options.callbacks.fetch,
2442
- headers: {
2443
- ...clientAttestation?.headers,
2444
- ...dpopHeaders
2445
- }
2810
+ callbacks: options.callbacks,
2811
+ headers: dpopHeaders
2446
2812
  });
2447
2813
  }
2448
2814
  });
@@ -2473,12 +2839,24 @@ async function createAuthorizationRequestUrl(options) {
2473
2839
  };
2474
2840
  }
2475
2841
  async function pushAuthorizationRequest(options) {
2476
- const fetchWithZod = createZodFetcher6(options.fetch);
2842
+ const fetchWithZod = createZodFetcher6(options.callbacks.fetch);
2477
2843
  if (options.authorizationRequest.request_uri) {
2478
2844
  throw new Oauth2Error(
2479
2845
  `Authorization request contains 'request_uri' parameter. This is not allowed for pushed authorization reuqests.`
2480
2846
  );
2481
2847
  }
2848
+ const headers = new Headers4({
2849
+ ...options.headers,
2850
+ "Content-Type": ContentType6.XWwwFormUrlencoded
2851
+ });
2852
+ await options.callbacks.clientAuthentication({
2853
+ url: options.pushedAuthorizationRequestEndpoint,
2854
+ method: "POST",
2855
+ authorizationServerMetadata: options.authorizationServerMetadata,
2856
+ body: options.authorizationRequest,
2857
+ contentType: ContentType6.XWwwFormUrlencoded,
2858
+ headers
2859
+ });
2482
2860
  const { response, result } = await fetchWithZod(
2483
2861
  zPushedAuthorizationResponse,
2484
2862
  ContentType6.Json,
@@ -2486,10 +2864,7 @@ async function pushAuthorizationRequest(options) {
2486
2864
  {
2487
2865
  method: "POST",
2488
2866
  body: objectToQueryParams4(options.authorizationRequest).toString(),
2489
- headers: {
2490
- ...options.headers,
2491
- "Content-Type": ContentType6.XWwwFormUrlencoded
2492
- }
2867
+ headers
2493
2868
  }
2494
2869
  );
2495
2870
  if (!response.ok || !result) {
@@ -2524,6 +2899,8 @@ var Oauth2Client = class {
2524
2899
  constructor(options) {
2525
2900
  this.options = options;
2526
2901
  }
2902
+ // TODO: add options to provide client metadata / algs supported by the client
2903
+ // so we can find the commonly supported algs and make it easier
2527
2904
  isDpopSupported(options) {
2528
2905
  if (!options.authorizationServerMetadata.dpop_signing_alg_values_supported || options.authorizationServerMetadata.dpop_signing_alg_values_supported.length === 0) {
2529
2906
  return {
@@ -2535,6 +2912,18 @@ var Oauth2Client = class {
2535
2912
  dpopSigningAlgValuesSupported: options.authorizationServerMetadata.dpop_signing_alg_values_supported
2536
2913
  };
2537
2914
  }
2915
+ isClientAttestationSupported(options) {
2916
+ if (!options.authorizationServerMetadata.token_endpoint_auth_methods_supported || !options.authorizationServerMetadata.token_endpoint_auth_methods_supported.includes(
2917
+ "attest_jwt_client_auth" /* ClientAttestationJwt */
2918
+ )) {
2919
+ return {
2920
+ supported: false
2921
+ };
2922
+ }
2923
+ return {
2924
+ supported: true
2925
+ };
2926
+ }
2538
2927
  async fetchAuthorizationServerMetadata(issuer) {
2539
2928
  return fetchAuthorizationServerMetadata(issuer, this.options.callbacks.fetch);
2540
2929
  }
@@ -2562,11 +2951,9 @@ var Oauth2Client = class {
2562
2951
  await this.sendAuthorizationChallengeRequest({
2563
2952
  authorizationServerMetadata: options.authorizationServerMetadata,
2564
2953
  additionalRequestPayload: options.additionalRequestPayload,
2565
- clientId: options.clientId,
2566
2954
  pkceCodeVerifier: pkce?.codeVerifier,
2567
2955
  scope: options.scope,
2568
2956
  resource: options.resource,
2569
- clientAttestation: options.clientAttestation,
2570
2957
  dpop: options.dpop
2571
2958
  });
2572
2959
  } catch (error) {
@@ -2599,7 +2986,6 @@ var Oauth2Client = class {
2599
2986
  scope: options.scope,
2600
2987
  pkceCodeVerifier: pkce?.codeVerifier,
2601
2988
  resource: options.resource,
2602
- clientAttestation: options.clientAttestation,
2603
2989
  dpop: options.dpop
2604
2990
  });
2605
2991
  }
@@ -2619,7 +3005,6 @@ var Oauth2Client = class {
2619
3005
  scope: options.scope,
2620
3006
  callbacks: this.options.callbacks,
2621
3007
  pkceCodeVerifier: options.pkceCodeVerifier,
2622
- clientAttestation: options.clientAttestation,
2623
3008
  dpop: options.dpop
2624
3009
  });
2625
3010
  }
@@ -2629,8 +3014,7 @@ var Oauth2Client = class {
2629
3014
  additionalRequestPayload,
2630
3015
  txCode,
2631
3016
  dpop,
2632
- resource,
2633
- clientAttestation
3017
+ resource
2634
3018
  }) {
2635
3019
  const result = await retrievePreAuthorizedCodeAccessToken({
2636
3020
  authorizationServerMetadata,
@@ -2642,8 +3026,7 @@ var Oauth2Client = class {
2642
3026
  tx_code: txCode
2643
3027
  },
2644
3028
  callbacks: this.options.callbacks,
2645
- dpop,
2646
- clientAttestation
3029
+ dpop
2647
3030
  });
2648
3031
  return result;
2649
3032
  }
@@ -2654,8 +3037,7 @@ var Oauth2Client = class {
2654
3037
  pkceCodeVerifier,
2655
3038
  redirectUri,
2656
3039
  resource,
2657
- dpop,
2658
- clientAttestation
3040
+ dpop
2659
3041
  }) {
2660
3042
  const result = await retrieveAuthorizationCodeAccessToken({
2661
3043
  authorizationServerMetadata,
@@ -2665,8 +3047,7 @@ var Oauth2Client = class {
2665
3047
  resource,
2666
3048
  callbacks: this.options.callbacks,
2667
3049
  dpop,
2668
- redirectUri,
2669
- clientAttestation
3050
+ redirectUri
2670
3051
  });
2671
3052
  return result;
2672
3053
  }
@@ -2675,8 +3056,7 @@ var Oauth2Client = class {
2675
3056
  additionalRequestPayload,
2676
3057
  refreshToken,
2677
3058
  resource,
2678
- dpop,
2679
- clientAttestation
3059
+ dpop
2680
3060
  }) {
2681
3061
  const result = await retrieveRefreshTokenAccessToken({
2682
3062
  authorizationServerMetadata,
@@ -2684,23 +3064,13 @@ var Oauth2Client = class {
2684
3064
  additionalRequestPayload,
2685
3065
  resource,
2686
3066
  callbacks: this.options.callbacks,
2687
- dpop,
2688
- clientAttestation
3067
+ dpop
2689
3068
  });
2690
3069
  return result;
2691
3070
  }
2692
3071
  async resourceRequest(options) {
2693
3072
  return resourceRequest(options);
2694
3073
  }
2695
- /**
2696
- * @todo move this to another class?
2697
- */
2698
- async createClientAttestationJwt(options) {
2699
- return await createClientAttestationJwt({
2700
- callbacks: this.options.callbacks,
2701
- ...options
2702
- });
2703
- }
2704
3074
  };
2705
3075
 
2706
3076
  // src/Oauth2ResourceServer.ts
@@ -2731,12 +3101,16 @@ export {
2731
3101
  Oauth2ServerErrorResponseError,
2732
3102
  PkceCodeChallengeMethod,
2733
3103
  SupportedAuthenticationScheme,
3104
+ SupportedClientAuthenticationMethod,
2734
3105
  authorizationCodeGrantIdentifier,
2735
3106
  calculateJwkThumbprint,
3107
+ clientAuthenticationAnonymous,
3108
+ clientAuthenticationClientAttestationJwt,
2736
3109
  clientAuthenticationClientSecretBasic,
2737
3110
  clientAuthenticationClientSecretPost,
2738
3111
  clientAuthenticationDynamic,
2739
3112
  clientAuthenticationNone,
3113
+ createClientAttestationJwt,
2740
3114
  decodeJwt,
2741
3115
  decodeJwtHeader,
2742
3116
  fetchAuthorizationServerMetadata,