@openid4vc/oauth2 0.3.0-alpha-20250324183425 → 0.3.0-alpha-20250325212250
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +2220 -492
- package/dist/index.d.ts +2220 -492
- package/dist/index.js +1105 -728
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1021 -647
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
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
|
|
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
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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
|
-
|
|
532
|
-
if (
|
|
533
|
-
return
|
|
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 (
|
|
561
|
-
return [
|
|
562
|
-
constants.issueSeparator
|
|
563
|
-
);
|
|
814
|
+
if (item.includes('"')) {
|
|
815
|
+
return `${acc}["${escapeQuotes(item)}"]`;
|
|
564
816
|
}
|
|
565
|
-
if (
|
|
566
|
-
return [
|
|
567
|
-
constants.issueSeparator
|
|
568
|
-
);
|
|
817
|
+
if (!constants.identifierRegex.test(item)) {
|
|
818
|
+
return `${acc}["${item}"]`;
|
|
569
819
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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 =
|
|
865
|
+
const formattedError = formatZodError(zodError);
|
|
588
866
|
this.message = `${message}
|
|
589
|
-
|
|
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
|
|
621
|
-
var
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
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:
|
|
913
|
+
code_challenge_methods_supported: z8.optional(z8.array(z8.string())),
|
|
629
914
|
// RFC9449
|
|
630
|
-
dpop_signing_alg_values_supported:
|
|
915
|
+
dpop_signing_alg_values_supported: z8.optional(z8.array(z8.string())),
|
|
631
916
|
// RFC9126
|
|
632
|
-
require_pushed_authorization_requests:
|
|
633
|
-
pushed_authorization_request_endpoint:
|
|
917
|
+
require_pushed_authorization_requests: z8.optional(z8.boolean()),
|
|
918
|
+
pushed_authorization_request_endpoint: z8.optional(zHttpsUrl2),
|
|
634
919
|
// RFC9068
|
|
635
|
-
introspection_endpoint:
|
|
636
|
-
introspection_endpoint_auth_methods_supported:
|
|
637
|
-
|
|
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:
|
|
924
|
+
introspection_endpoint_auth_signing_alg_values_supported: z8.optional(z8.array(zAlgValueNotNone)),
|
|
640
925
|
// FiPA (no RFC yet)
|
|
641
|
-
authorization_challenge_endpoint:
|
|
926
|
+
authorization_challenge_endpoint: z8.optional(zHttpsUrl2),
|
|
642
927
|
// From OpenID4VCI specification
|
|
643
|
-
pre_authorized_grant_anonymous_access_supported:
|
|
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
|
|
794
|
-
import
|
|
795
|
-
var zAccessTokenProfileJwtHeader =
|
|
1027
|
+
import { zInteger as zInteger3 } from "@openid4vc/utils";
|
|
1028
|
+
import z9 from "zod";
|
|
1029
|
+
var zAccessTokenProfileJwtHeader = z9.object({
|
|
796
1030
|
...zJwtHeader.shape,
|
|
797
|
-
typ:
|
|
1031
|
+
typ: z9.enum(["application/at+jwt", "at+jwt"])
|
|
798
1032
|
}).passthrough();
|
|
799
|
-
var zAccessTokenProfileJwtPayload =
|
|
1033
|
+
var zAccessTokenProfileJwtPayload = z9.object({
|
|
800
1034
|
...zJwtPayload.shape,
|
|
801
|
-
iss:
|
|
802
|
-
exp:
|
|
803
|
-
iat:
|
|
804
|
-
aud:
|
|
805
|
-
sub:
|
|
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:
|
|
808
|
-
jti:
|
|
1041
|
+
client_id: z9.optional(z9.string()),
|
|
1042
|
+
jti: z9.string(),
|
|
809
1043
|
// SHOULD be included in the authorization request contained it
|
|
810
|
-
scope:
|
|
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
|
|
1100
|
+
dateToSeconds as dateToSeconds4,
|
|
867
1101
|
decodeUtf8String as decodeUtf8String2,
|
|
868
|
-
encodeToBase64Url as
|
|
869
|
-
parseWithErrorHandling as
|
|
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
|
|
874
|
-
import
|
|
875
|
-
var zDpopJwtPayload =
|
|
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:
|
|
878
|
-
htu:
|
|
1111
|
+
iat: zInteger4,
|
|
1112
|
+
htu: zHttpsUrl3,
|
|
879
1113
|
htm: zHttpMethod,
|
|
880
|
-
jti:
|
|
1114
|
+
jti: z10.string(),
|
|
881
1115
|
// Only required when presenting in combination with access token
|
|
882
|
-
ath:
|
|
1116
|
+
ath: z10.optional(z10.string())
|
|
883
1117
|
}).passthrough();
|
|
884
|
-
var zDpopJwtHeader =
|
|
1118
|
+
var zDpopJwtHeader = z10.object({
|
|
885
1119
|
...zJwtHeader.shape,
|
|
886
|
-
typ:
|
|
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 =
|
|
1134
|
+
ath = encodeToBase64Url3(await options.callbacks.hash(decodeUtf8String2(options.accessToken), "sha-256" /* Sha256 */));
|
|
901
1135
|
}
|
|
902
|
-
const header =
|
|
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 =
|
|
1141
|
+
const payload = parseWithErrorHandling6(zDpopJwtPayload, {
|
|
908
1142
|
htu: htuFromRequestUrl(options.request.url),
|
|
909
|
-
iat:
|
|
1143
|
+
iat: dateToSeconds4(options.issuedAt),
|
|
910
1144
|
htm: options.request.method,
|
|
911
|
-
jti:
|
|
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
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
if (
|
|
935
|
-
|
|
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 (
|
|
1180
|
+
if (options.request.method !== payload.htm) {
|
|
938
1181
|
throw new Oauth2Error(
|
|
939
|
-
`Dpop jwt contains
|
|
1182
|
+
`Dpop jwt contains htm value '${payload.htm}', but expected htm value '${options.request.method}'`
|
|
940
1183
|
);
|
|
941
1184
|
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
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 (
|
|
960
|
-
|
|
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 !==
|
|
1205
|
+
if (options.expectedJwkThumbprint && options.expectedJwkThumbprint !== jwkThumbprint) {
|
|
970
1206
|
throw new Oauth2Error(
|
|
971
|
-
`Dpop is signed with jwk with thumbprint value '${
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1135
|
-
import
|
|
1136
|
-
var zTokenIntrospectionRequest =
|
|
1137
|
-
token:
|
|
1138
|
-
token_type_hint:
|
|
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 =
|
|
1141
|
-
active:
|
|
1142
|
-
scope:
|
|
1143
|
-
client_id:
|
|
1144
|
-
username:
|
|
1145
|
-
token_type:
|
|
1146
|
-
exp:
|
|
1147
|
-
iat:
|
|
1148
|
-
nbf:
|
|
1149
|
-
sub:
|
|
1150
|
-
aud:
|
|
1151
|
-
iss:
|
|
1152
|
-
jti:
|
|
1153
|
-
cnf:
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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,
|
|
1374
|
-
const endpointType = url ===
|
|
1375
|
-
const method = getSupportedClientAuthenticationMethod(
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
1418
|
-
iat:
|
|
1419
|
-
exp:
|
|
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:
|
|
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.
|
|
1714
|
+
cnf: options.dpop ? {
|
|
1427
1715
|
jkt: await calculateJwkThumbprint({
|
|
1428
1716
|
hashAlgorithm: "sha-256" /* Sha256 */,
|
|
1429
1717
|
hashCallback: options.callbacks.hash,
|
|
1430
|
-
jwk: options.
|
|
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
|
|
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
|
-
|
|
1462
|
-
|
|
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":
|
|
1741
|
+
"pre-authorized_code": z13.optional(z13.string()),
|
|
1465
1742
|
// Authorization code flow
|
|
1466
|
-
code:
|
|
1467
|
-
redirect_uri:
|
|
1743
|
+
code: z13.optional(z13.string()),
|
|
1744
|
+
redirect_uri: z13.string().url().optional(),
|
|
1468
1745
|
// Refresh token grant
|
|
1469
|
-
refresh_token:
|
|
1470
|
-
resource:
|
|
1471
|
-
code_verifier:
|
|
1472
|
-
grant_type:
|
|
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
|
-
|
|
1754
|
+
z13.string()
|
|
1478
1755
|
])
|
|
1479
1756
|
}).passthrough(),
|
|
1480
|
-
|
|
1481
|
-
tx_code:
|
|
1757
|
+
z13.object({
|
|
1758
|
+
tx_code: z13.optional(z13.string()),
|
|
1482
1759
|
// user_pin is from OpenID4VCI draft 11
|
|
1483
|
-
user_pin:
|
|
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 =
|
|
1494
|
-
access_token:
|
|
1495
|
-
token_type:
|
|
1496
|
-
expires_in:
|
|
1497
|
-
scope:
|
|
1498
|
-
state:
|
|
1499
|
-
refresh_token:
|
|
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:
|
|
1502
|
-
c_nonce_expires_in:
|
|
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:
|
|
1505
|
-
|
|
1506
|
-
//
|
|
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 =
|
|
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
|
-
${
|
|
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
|
-
|
|
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
|
|
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 ??
|
|
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
|
|
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) :
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
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
|
-
|
|
1700
|
-
|
|
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
|
-
|
|
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
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
}
|
|
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
|
|
2105
|
+
import { parseWithErrorHandling as parseWithErrorHandling10 } from "@openid4vc/utils";
|
|
1761
2106
|
|
|
1762
2107
|
// src/authorization-challenge/z-authorization-challenge.ts
|
|
1763
|
-
import { zInteger as
|
|
1764
|
-
import
|
|
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
|
|
1768
|
-
import
|
|
1769
|
-
var zAuthorizationRequest =
|
|
1770
|
-
response_type:
|
|
1771
|
-
client_id:
|
|
1772
|
-
issuer_state:
|
|
1773
|
-
redirect_uri:
|
|
1774
|
-
resource:
|
|
1775
|
-
scope:
|
|
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:
|
|
1778
|
-
code_challenge:
|
|
1779
|
-
code_challenge_method:
|
|
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 =
|
|
1782
|
-
request_uri:
|
|
1783
|
-
client_id:
|
|
2126
|
+
var zPushedAuthorizationRequest = z14.object({
|
|
2127
|
+
request_uri: z14.string(),
|
|
2128
|
+
client_id: z14.string()
|
|
1784
2129
|
}).passthrough();
|
|
1785
|
-
var zPushedAuthorizationResponse =
|
|
1786
|
-
request_uri:
|
|
1787
|
-
expires_in:
|
|
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 =
|
|
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:
|
|
1797
|
-
auth_session:
|
|
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:
|
|
2144
|
+
presentation_during_issuance_session: z15.optional(z15.string())
|
|
1800
2145
|
}).passthrough();
|
|
1801
|
-
var zAuthorizationChallengeResponse =
|
|
1802
|
-
authorization_code:
|
|
2146
|
+
var zAuthorizationChallengeResponse = z15.object({
|
|
2147
|
+
authorization_code: z15.string()
|
|
1803
2148
|
}).passthrough();
|
|
1804
|
-
var zAuthorizationChallengeErrorResponse =
|
|
2149
|
+
var zAuthorizationChallengeErrorResponse = z15.object({
|
|
1805
2150
|
...zOauth2ErrorResponse.shape,
|
|
1806
|
-
auth_session:
|
|
1807
|
-
request_uri:
|
|
1808
|
-
expires_in:
|
|
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:
|
|
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 =
|
|
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 =
|
|
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 {
|
|
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
|
|
1841
|
-
zAuthorizationChallengeRequest,
|
|
2221
|
+
const parsedAuthorizationChallengeRequest = zAuthorizationChallengeRequest.safeParse(
|
|
1842
2222
|
options.authorizationChallengeRequest
|
|
1843
2223
|
);
|
|
1844
|
-
|
|
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/
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
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
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
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
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
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
|
-
|
|
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
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
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
|
-
|
|
1944
|
-
|
|
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
|
-
|
|
1948
|
-
|
|
2331
|
+
jwk: verifyDpopResult?.header.jwk,
|
|
2332
|
+
jwkThumbprint: verifyDpopResult?.jwkThumbprint ?? options.jwkThumbprint
|
|
1949
2333
|
};
|
|
1950
2334
|
}
|
|
1951
2335
|
|
|
1952
|
-
// src/
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
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
|
-
|
|
1965
|
-
|
|
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
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
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
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
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
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
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
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2384
|
+
authorizationRequest,
|
|
2385
|
+
dpop,
|
|
2386
|
+
clientAttestation
|
|
2008
2387
|
};
|
|
2009
2388
|
}
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
2131
|
-
|
|
2132
|
-
|
|
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
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
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
|
|
2237
|
-
|
|
2238
|
-
...
|
|
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:
|
|
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
|
-
|
|
2437
|
-
|
|
2438
|
-
...clientAttestation?.headers
|
|
2439
|
-
},
|
|
2807
|
+
authorizationServerMetadata,
|
|
2808
|
+
authorizationRequest,
|
|
2440
2809
|
pushedAuthorizationRequestEndpoint,
|
|
2441
|
-
|
|
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,
|