@openeudi/openid4vp 0.11.1 → 0.13.0
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/README.md +140 -4
- package/dist/index.cjs +742 -113
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +165 -8
- package/dist/index.d.ts +165 -8
- package/dist/index.js +741 -115
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -79,10 +79,49 @@ interface HaipQueryInput {
|
|
|
79
79
|
claims: string[];
|
|
80
80
|
trustedAuthorities?: TrustedAuthoritiesQuery[];
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Input to `buildCredentialSetQuery` — a DCQL `credential_sets` disjunction
|
|
84
|
+
* offering the wallet several alternative credentials for the same question.
|
|
85
|
+
*/
|
|
86
|
+
interface CredentialSetQueryInput {
|
|
87
|
+
/**
|
|
88
|
+
* The alternatives, **most-preferred first**. Option order is a privacy
|
|
89
|
+
* signal, not cosmetic: list the credential that discloses least first.
|
|
90
|
+
* Wallets are not obliged to honour the order.
|
|
91
|
+
*/
|
|
92
|
+
options: HaipQueryInput[];
|
|
93
|
+
/** Whether the wallet must satisfy one option. Default: `true`. */
|
|
94
|
+
required?: boolean;
|
|
95
|
+
}
|
|
82
96
|
|
|
83
97
|
declare const HAIP_DOCTYPE_NAMESPACES: Record<string, string>;
|
|
84
98
|
declare function buildHaipQuery(input: HaipQueryInput): DcqlQuery;
|
|
99
|
+
/**
|
|
100
|
+
* Build a DCQL query offering the wallet a choice between several credentials —
|
|
101
|
+
* a `credential_sets` disjunction.
|
|
102
|
+
*
|
|
103
|
+
* The motivating case is a proof-of-age check: ask for a Proof-of-Age
|
|
104
|
+
* attestation (`mso_mdoc`, `age_over_18` in namespace `eu.europa.ec.av.1`) OR a
|
|
105
|
+
* PID (`dc+sd-jwt`, `birth_date`), because the PID carries no age attribute and
|
|
106
|
+
* proof-of-age attestations have almost no issuer coverage yet. The wallet
|
|
107
|
+
* satisfies ONE option and returns ONE presentation.
|
|
108
|
+
*
|
|
109
|
+
* This is deliberately NOT HAIP-minimal: `validateHaipQuery` rejects
|
|
110
|
+
* `credential_sets`, and `isHaipQuery` returns `false` for what this produces.
|
|
111
|
+
* That boundary is intentional — use `validateCredentialSetQuery` instead.
|
|
112
|
+
*
|
|
113
|
+
* Each option is built through {@link buildHaipQuery}, so per-credential
|
|
114
|
+
* validation and its `HaipValidationError` codes are identical.
|
|
115
|
+
*/
|
|
116
|
+
declare function buildCredentialSetQuery(input: CredentialSetQueryInput): DcqlQuery;
|
|
85
117
|
declare function validateHaipQuery(query: DcqlQuery): void;
|
|
118
|
+
/**
|
|
119
|
+
* Validate a DCQL query that offers the wallet a choice — the profile
|
|
120
|
+
* `buildCredentialSetQuery` produces. Per-credential rules are identical to
|
|
121
|
+
* HAIP-minimal; the difference is that `credential_sets` is REQUIRED here
|
|
122
|
+
* rather than forbidden.
|
|
123
|
+
*/
|
|
124
|
+
declare function validateCredentialSetQuery(query: DcqlQuery): void;
|
|
86
125
|
declare function isHaipQuery(query: DcqlQuery): boolean;
|
|
87
126
|
|
|
88
127
|
interface IssuerInfo {
|
|
@@ -228,8 +267,9 @@ declare class StaticTrustStore implements TrustStore {
|
|
|
228
267
|
}
|
|
229
268
|
/**
|
|
230
269
|
* Combines multiple `TrustStore` instances. Children are queried in parallel;
|
|
231
|
-
* results concatenate in child-order; duplicate anchors
|
|
232
|
-
*
|
|
270
|
+
* results concatenate in child-order; duplicate anchors (byte-identical DER)
|
|
271
|
+
* are dropped, first occurrence wins. Distinct certificates that share a key
|
|
272
|
+
* (e.g. a re-issued root) are both kept, so neither validity window is lost.
|
|
233
273
|
*/
|
|
234
274
|
declare class CompositeTrustStore implements TrustStore {
|
|
235
275
|
private readonly stores;
|
|
@@ -291,7 +331,9 @@ interface PresentationResult {
|
|
|
291
331
|
interface ParseOptions {
|
|
292
332
|
/**
|
|
293
333
|
* DER-encoded issuer leaf certificates used for byte-equality trust check.
|
|
294
|
-
* Kept for 0.4.0 compatibility.
|
|
334
|
+
* Kept for 0.4.0 compatibility. No chain building: a credential signed by a
|
|
335
|
+
* document signer issued under a listed root (e.g. an IACA) is rejected on
|
|
336
|
+
* this path — use `trustStore` for that.
|
|
295
337
|
* @deprecated since 0.5.0. Use `trustStore: new StaticTrustStore([...])`
|
|
296
338
|
* with root/intermediate CAs for RFC 5280 chain validation. Scheduled
|
|
297
339
|
* for removal in 1.0.0.
|
|
@@ -332,6 +374,15 @@ interface ParseOptions {
|
|
|
332
374
|
* Trust anchor resolver. When provided, the library performs RFC 5280
|
|
333
375
|
* chain validation and ignores `trustedCertificates`. When unset, the
|
|
334
376
|
* library falls back to 0.4.0 byte-equality against `trustedCertificates`.
|
|
377
|
+
*
|
|
378
|
+
* The credential's signer certificate (`x5c[0]` / `x5chain[0]`) is trusted
|
|
379
|
+
* when it either IS an anchor (byte-identical DER) or chains to one through
|
|
380
|
+
* verified signatures, using the remaining `x5c` / `x5chain` entries as
|
|
381
|
+
* untrusted path candidates. Anchors may be roots such as an ISO 18013-5
|
|
382
|
+
* IACA, or the document-signer certificate itself. For `mso_mdoc`, built
|
|
383
|
+
* chains must also satisfy the ISO 18013-5 Annex B certificate profile
|
|
384
|
+
* (DS EKU `1.0.18013.5.1.2`, mandatory keyUsage). Every certificate must be
|
|
385
|
+
* valid now and at the credential's issuance time (MSO `signed` / `iat`).
|
|
335
386
|
*/
|
|
336
387
|
trustStore?: TrustStore;
|
|
337
388
|
/**
|
|
@@ -339,7 +390,13 @@ interface ParseOptions {
|
|
|
339
390
|
* ship in 0.5.0 workstream A.2 — passing them today throws.
|
|
340
391
|
*/
|
|
341
392
|
revocationPolicy?: 'skip' | 'prefer' | 'require';
|
|
342
|
-
/**
|
|
393
|
+
/**
|
|
394
|
+
* HTTP transport for CRL/OCSP/LOTL fetches. Defaults to a guarded fetcher
|
|
395
|
+
* (`createGuardedFetcher({ allowHttp: true })`): no private/loopback/metadata
|
|
396
|
+
* targets, manual re-validated redirects (max 3), 15 s timeout, 16 MiB cap.
|
|
397
|
+
* A caller-supplied fetcher replaces those guards entirely — wrap it with
|
|
398
|
+
* `createGuardedFetcher({ fetch: yourFetcher })` to keep them.
|
|
399
|
+
*/
|
|
343
400
|
fetcher?: Fetcher;
|
|
344
401
|
/** Cache for CRL/OCSP/LOTL artefacts. Defaults to `new InMemoryCache()`. */
|
|
345
402
|
cache?: Cache;
|
|
@@ -366,6 +423,15 @@ interface ParseOptions {
|
|
|
366
423
|
* secure path.
|
|
367
424
|
*/
|
|
368
425
|
trustedIssuerJwks?: JsonWebKey[];
|
|
426
|
+
/**
|
|
427
|
+
* Accept the legacy `vc+sd-jwt` media type in the SD-JWT VC issuer JWT's
|
|
428
|
+
* `typ` header, in addition to `dc+sd-jwt`. Default `false`.
|
|
429
|
+
*
|
|
430
|
+
* @deprecated draft-ietf-oauth-sd-jwt-vc-19 removed `vc+sd-jwt`; only
|
|
431
|
+
* `dc+sd-jwt` is conformant. This exists solely to keep interoperating
|
|
432
|
+
* with wallets that have not migrated yet and will be removed in 1.0.0.
|
|
433
|
+
*/
|
|
434
|
+
allowLegacyVcSdJwtTyp?: boolean;
|
|
369
435
|
}
|
|
370
436
|
interface ICredentialParser {
|
|
371
437
|
readonly format: CredentialFormat;
|
|
@@ -574,7 +640,7 @@ declare class MalformedCredentialError extends Error {
|
|
|
574
640
|
declare class NonceValidationError extends Error {
|
|
575
641
|
constructor(message?: string);
|
|
576
642
|
}
|
|
577
|
-
type HaipValidationCode = 'EMPTY_QUERY' | 'UNSUPPORTED_FORMAT' | 'MISSING_SDJWT_META' | 'MISSING_MDOC_META' | 'NO_CLAIMS' | 'CLAIM_SETS_DISALLOWED' | 'CREDENTIAL_SETS_DISALLOWED';
|
|
643
|
+
type HaipValidationCode = 'EMPTY_QUERY' | 'UNSUPPORTED_FORMAT' | 'MISSING_SDJWT_META' | 'MISSING_MDOC_META' | 'NO_CLAIMS' | 'CLAIM_SETS_DISALLOWED' | 'CREDENTIAL_SETS_DISALLOWED' | 'EMPTY_OPTIONS' | 'DUPLICATE_CREDENTIAL_ID' | 'MISSING_CREDENTIAL_SETS' | 'UNKNOWN_OPTION_REFERENCE';
|
|
578
644
|
declare class HaipValidationError extends Error {
|
|
579
645
|
readonly code: HaipValidationCode;
|
|
580
646
|
readonly credentialId?: string;
|
|
@@ -589,7 +655,7 @@ declare abstract class OpenID4VPError extends Error {
|
|
|
589
655
|
declare class TrustAnchorNotFoundError extends OpenID4VPError {
|
|
590
656
|
readonly code: "trust_anchor_not_found";
|
|
591
657
|
}
|
|
592
|
-
type ChainErrorReason = 'signature' | 'validity' | 'name_constraints' | 'key_usage' | 'basic_constraints' | 'path_length' | 'algorithm_disallowed' | 'aki_ski_mismatch';
|
|
658
|
+
type ChainErrorReason = 'signature' | 'validity' | 'name_constraints' | 'key_usage' | 'extended_key_usage' | 'basic_constraints' | 'path_length' | 'algorithm_disallowed' | 'aki_ski_mismatch';
|
|
593
659
|
declare class CertificateChainError extends OpenID4VPError {
|
|
594
660
|
readonly code: "chain_invalid";
|
|
595
661
|
readonly reason: ChainErrorReason;
|
|
@@ -624,6 +690,19 @@ declare class LotlFetchError extends OpenID4VPError {
|
|
|
624
690
|
declare class LotlSignatureError extends OpenID4VPError {
|
|
625
691
|
readonly code: "lotl_signature_invalid";
|
|
626
692
|
}
|
|
693
|
+
/**
|
|
694
|
+
* The environment is not configured to verify trusted-list signatures at all —
|
|
695
|
+
* distinct from a list whose signature did not verify.
|
|
696
|
+
*
|
|
697
|
+
* `xmldsigjs` needs a WebCrypto engine registered once per process, and this
|
|
698
|
+
* library deliberately leaves that to the consumer rather than picking a
|
|
699
|
+
* provider on their behalf. Without it, no trusted list can ever verify, so
|
|
700
|
+
* this is raised eagerly instead of degrading per-list: a configuration fault
|
|
701
|
+
* reported as a trust failure sends people bisecting valid XML.
|
|
702
|
+
*/
|
|
703
|
+
declare class LotlConfigurationError extends OpenID4VPError {
|
|
704
|
+
readonly code: "lotl_configuration_invalid";
|
|
705
|
+
}
|
|
627
706
|
type SignedRequestBuildErrorCode = 'empty_cert_chain' | 'hostname_cert_mismatch' | 'signing_key_cert_mismatch' | 'missing_encryption_jwk' | 'missing_encryption_alg' | 'missing_vp_formats' | 'unsupported_signing_alg' | 'empty_supported_enc_values' | 'missing_hostname' | 'self_signed_leaf';
|
|
628
707
|
declare class SignedRequestBuildError extends OpenID4VPError {
|
|
629
708
|
readonly code: SignedRequestBuildErrorCode;
|
|
@@ -655,6 +734,84 @@ declare class MultipleCredentialsNotSupportedError extends OpenID4VPError {
|
|
|
655
734
|
readonly presentationCount: number;
|
|
656
735
|
constructor(entryCount: number, presentationCount: number);
|
|
657
736
|
}
|
|
737
|
+
type GuardedFetchRejectReason = 'invalid_url' | 'insecure_scheme' | 'private_address' | 'dns_resolution_failed' | 'too_many_redirects' | 'invalid_redirect' | 'response_too_large' | 'timeout';
|
|
738
|
+
/**
|
|
739
|
+
* A request was refused (or cut off) by the guarded fetcher before an
|
|
740
|
+
* untrusted endpoint could be reached or could exhaust resources.
|
|
741
|
+
*/
|
|
742
|
+
declare class GuardedFetchError extends OpenID4VPError {
|
|
743
|
+
readonly code: "guarded_fetch_rejected";
|
|
744
|
+
readonly reason: GuardedFetchRejectReason;
|
|
745
|
+
readonly url: string;
|
|
746
|
+
constructor(reason: GuardedFetchRejectReason, message: string, options: {
|
|
747
|
+
url: string;
|
|
748
|
+
cause?: Error;
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Resolves a hostname to every IP address it currently maps to. Must return
|
|
754
|
+
* all records (A and AAAA): the guard rejects the host if ANY of them is
|
|
755
|
+
* non-public, and treats an empty result as a resolution failure.
|
|
756
|
+
*/
|
|
757
|
+
type HostLookup = (hostname: string) => Promise<readonly string[]>;
|
|
758
|
+
interface GuardedFetcherOptions {
|
|
759
|
+
/**
|
|
760
|
+
* Underlying transport. Defaults to `globalThis.fetch`, resolved at call
|
|
761
|
+
* time. Must honour `redirect: 'manual'` and expose the `Location` header
|
|
762
|
+
* of 3xx responses (Node's fetch/undici does; browsers return an opaque
|
|
763
|
+
* redirect, which the guard rejects).
|
|
764
|
+
*/
|
|
765
|
+
fetch?: Fetcher;
|
|
766
|
+
/**
|
|
767
|
+
* DNS resolver used for the private-address check. Defaults to
|
|
768
|
+
* `node:dns` `lookup(host, { all: true })` when available. In runtimes
|
|
769
|
+
* without `node:dns` (browsers, some edge workers) only IP-literal and
|
|
770
|
+
* `localhost` targets are checked — inject a resolver there if needed.
|
|
771
|
+
*/
|
|
772
|
+
lookup?: HostLookup;
|
|
773
|
+
/** Permit `http:` targets. Default `false` (SD-JWT VC draft-19: HTTPS only). */
|
|
774
|
+
allowHttp?: boolean;
|
|
775
|
+
/** Permit loopback / private / link-local / metadata targets. Default `false`. */
|
|
776
|
+
allowPrivateNetworks?: boolean;
|
|
777
|
+
/** Maximum redirects followed; every hop is re-validated. Default 3. */
|
|
778
|
+
maxRedirects?: number;
|
|
779
|
+
/** Wall-clock budget for the whole exchange (DNS, redirects, body). Default 15 000 ms. */
|
|
780
|
+
timeoutMs?: number;
|
|
781
|
+
/** Maximum response body size, enforced while streaming. Default 16 MiB. */
|
|
782
|
+
maxResponseBytes?: number;
|
|
783
|
+
}
|
|
784
|
+
declare const GUARDED_FETCH_DEFAULTS: {
|
|
785
|
+
readonly maxRedirects: 3;
|
|
786
|
+
readonly timeoutMs: 15000;
|
|
787
|
+
readonly maxResponseBytes: number;
|
|
788
|
+
};
|
|
789
|
+
/**
|
|
790
|
+
* Builds a {@link Fetcher} that applies the HTTP-retrieval rules of
|
|
791
|
+
* draft-ietf-oauth-sd-jwt-vc-19 to every request:
|
|
792
|
+
*
|
|
793
|
+
* - HTTPS only (unless `allowHttp`), and never an https → http downgrade
|
|
794
|
+
* - no loopback / private / link-local / cloud-metadata targets, checked
|
|
795
|
+
* on the URL host AND every address it resolves to (IPv4, IPv6,
|
|
796
|
+
* IPv4-mapped/compatible, NAT64, 6to4)
|
|
797
|
+
* - redirects handled manually, bounded, and re-validated hop by hop
|
|
798
|
+
* - a single timeout covering DNS, every hop and the body
|
|
799
|
+
* - a response-size cap enforced while streaming
|
|
800
|
+
*
|
|
801
|
+
* **DNS rebinding.** The guard resolves the host, validates the addresses,
|
|
802
|
+
* then hands the *hostname* to the transport, which resolves it again. An
|
|
803
|
+
* attacker-controlled DNS server can answer differently the second time
|
|
804
|
+
* (TOCTOU). Closing that gap requires pinning the validated address at the
|
|
805
|
+
* socket layer, which `fetch` does not expose portably. Deployments that
|
|
806
|
+
* dereference attacker-influenced URLs should additionally route egress
|
|
807
|
+
* through a filtering proxy or inject a transport whose connector validates
|
|
808
|
+
* the connected address (e.g. an undici `Agent` with a checking
|
|
809
|
+
* `connect.lookup`).
|
|
810
|
+
*
|
|
811
|
+
* Caching is out of scope here: the trust module caches CRL/OCSP/LOTL
|
|
812
|
+
* artefacts through its own {@link Cache} plug.
|
|
813
|
+
*/
|
|
814
|
+
declare function createGuardedFetcher(options?: GuardedFetcherOptions): Fetcher;
|
|
658
815
|
|
|
659
816
|
interface LotlTrustStoreOptions {
|
|
660
817
|
/** Override the bundled signing anchors. Empty is rejected. */
|
|
@@ -693,6 +850,6 @@ declare class LotlTrustStore implements TrustStore {
|
|
|
693
850
|
private refresh;
|
|
694
851
|
}
|
|
695
852
|
|
|
696
|
-
declare const VERSION = "0.
|
|
853
|
+
declare const VERSION = "0.13.0";
|
|
697
854
|
|
|
698
|
-
export { type AuthorizationRequest, type AuthorizationRequestInput, type AuthorizationResponse, type Cache, CertificateChainError, type ChainErrorReason, CompositeTrustStore, type CredentialClaims, type CredentialFormat, DecryptionFailedError, type EncryptedResponse, ExpiredCredentialError, type Fetcher, HAIP_DOCTYPE_NAMESPACES, type HaipQueryInput, type HaipValidationCode, HaipValidationError, type ICredentialParser, InMemoryCache, InvalidSignatureError, type IssuerInfo, type LotlAnchorMetadata, LotlFetchError, LotlSignatureError, LotlTrustStore, type LotlTrustStoreOptions, MalformedCredentialError, MdocParser, MissingDecryptionKeyError, MissingVerifierEncryptionKeyError, MultipleCredentialsNotSupportedError, NonceValidationError, OpenID4VPError, type ParseOptions, type PresentationResult, RevocationCheckFailedError, RevokedCertificateError, SdJwtParser, type SignedAuthorizationRequest, type SignedAuthorizationRequestInput, SignedRequestBuildError, type SignedRequestBuildErrorCode, StaticTrustStore, type TrustAnchor, TrustAnchorNotFoundError, type TrustStore, type TrustStoreHint, type TrustStoreInput, UnsupportedFormatError, UnsupportedJweError, VERSION, type VerifyAuthorizationResponseOptions, type VerifyOptions, type VerifyResult, buildHaipQuery, buildOid4vpSessionTranscript, buildOpenID4VPHandoverSessionTranscript, createAuthorizationRequest, createSignedAuthorizationRequest, decryptAuthorizationResponse, isHaipQuery, parsePresentation, validateHaipQuery, verifyAuthorizationResponse, verifyPresentation };
|
|
855
|
+
export { type AuthorizationRequest, type AuthorizationRequestInput, type AuthorizationResponse, type Cache, CertificateChainError, type ChainErrorReason, CompositeTrustStore, type CredentialClaims, type CredentialFormat, type CredentialSetQueryInput, DecryptionFailedError, type EncryptedResponse, ExpiredCredentialError, type Fetcher, GUARDED_FETCH_DEFAULTS, GuardedFetchError, type GuardedFetchRejectReason, type GuardedFetcherOptions, HAIP_DOCTYPE_NAMESPACES, type HaipQueryInput, type HaipValidationCode, HaipValidationError, type HostLookup, type ICredentialParser, InMemoryCache, InvalidSignatureError, type IssuerInfo, type LotlAnchorMetadata, LotlConfigurationError, LotlFetchError, LotlSignatureError, LotlTrustStore, type LotlTrustStoreOptions, MalformedCredentialError, MdocParser, MissingDecryptionKeyError, MissingVerifierEncryptionKeyError, MultipleCredentialsNotSupportedError, NonceValidationError, OpenID4VPError, type ParseOptions, type PresentationResult, RevocationCheckFailedError, RevokedCertificateError, SdJwtParser, type SignedAuthorizationRequest, type SignedAuthorizationRequestInput, SignedRequestBuildError, type SignedRequestBuildErrorCode, StaticTrustStore, type TrustAnchor, TrustAnchorNotFoundError, type TrustStore, type TrustStoreHint, type TrustStoreInput, UnsupportedFormatError, UnsupportedJweError, VERSION, type VerifyAuthorizationResponseOptions, type VerifyOptions, type VerifyResult, buildCredentialSetQuery, buildHaipQuery, buildOid4vpSessionTranscript, buildOpenID4VPHandoverSessionTranscript, createAuthorizationRequest, createGuardedFetcher, createSignedAuthorizationRequest, decryptAuthorizationResponse, isHaipQuery, parsePresentation, validateCredentialSetQuery, validateHaipQuery, verifyAuthorizationResponse, verifyPresentation };
|
package/dist/index.d.ts
CHANGED
|
@@ -79,10 +79,49 @@ interface HaipQueryInput {
|
|
|
79
79
|
claims: string[];
|
|
80
80
|
trustedAuthorities?: TrustedAuthoritiesQuery[];
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Input to `buildCredentialSetQuery` — a DCQL `credential_sets` disjunction
|
|
84
|
+
* offering the wallet several alternative credentials for the same question.
|
|
85
|
+
*/
|
|
86
|
+
interface CredentialSetQueryInput {
|
|
87
|
+
/**
|
|
88
|
+
* The alternatives, **most-preferred first**. Option order is a privacy
|
|
89
|
+
* signal, not cosmetic: list the credential that discloses least first.
|
|
90
|
+
* Wallets are not obliged to honour the order.
|
|
91
|
+
*/
|
|
92
|
+
options: HaipQueryInput[];
|
|
93
|
+
/** Whether the wallet must satisfy one option. Default: `true`. */
|
|
94
|
+
required?: boolean;
|
|
95
|
+
}
|
|
82
96
|
|
|
83
97
|
declare const HAIP_DOCTYPE_NAMESPACES: Record<string, string>;
|
|
84
98
|
declare function buildHaipQuery(input: HaipQueryInput): DcqlQuery;
|
|
99
|
+
/**
|
|
100
|
+
* Build a DCQL query offering the wallet a choice between several credentials —
|
|
101
|
+
* a `credential_sets` disjunction.
|
|
102
|
+
*
|
|
103
|
+
* The motivating case is a proof-of-age check: ask for a Proof-of-Age
|
|
104
|
+
* attestation (`mso_mdoc`, `age_over_18` in namespace `eu.europa.ec.av.1`) OR a
|
|
105
|
+
* PID (`dc+sd-jwt`, `birth_date`), because the PID carries no age attribute and
|
|
106
|
+
* proof-of-age attestations have almost no issuer coverage yet. The wallet
|
|
107
|
+
* satisfies ONE option and returns ONE presentation.
|
|
108
|
+
*
|
|
109
|
+
* This is deliberately NOT HAIP-minimal: `validateHaipQuery` rejects
|
|
110
|
+
* `credential_sets`, and `isHaipQuery` returns `false` for what this produces.
|
|
111
|
+
* That boundary is intentional — use `validateCredentialSetQuery` instead.
|
|
112
|
+
*
|
|
113
|
+
* Each option is built through {@link buildHaipQuery}, so per-credential
|
|
114
|
+
* validation and its `HaipValidationError` codes are identical.
|
|
115
|
+
*/
|
|
116
|
+
declare function buildCredentialSetQuery(input: CredentialSetQueryInput): DcqlQuery;
|
|
85
117
|
declare function validateHaipQuery(query: DcqlQuery): void;
|
|
118
|
+
/**
|
|
119
|
+
* Validate a DCQL query that offers the wallet a choice — the profile
|
|
120
|
+
* `buildCredentialSetQuery` produces. Per-credential rules are identical to
|
|
121
|
+
* HAIP-minimal; the difference is that `credential_sets` is REQUIRED here
|
|
122
|
+
* rather than forbidden.
|
|
123
|
+
*/
|
|
124
|
+
declare function validateCredentialSetQuery(query: DcqlQuery): void;
|
|
86
125
|
declare function isHaipQuery(query: DcqlQuery): boolean;
|
|
87
126
|
|
|
88
127
|
interface IssuerInfo {
|
|
@@ -228,8 +267,9 @@ declare class StaticTrustStore implements TrustStore {
|
|
|
228
267
|
}
|
|
229
268
|
/**
|
|
230
269
|
* Combines multiple `TrustStore` instances. Children are queried in parallel;
|
|
231
|
-
* results concatenate in child-order; duplicate anchors
|
|
232
|
-
*
|
|
270
|
+
* results concatenate in child-order; duplicate anchors (byte-identical DER)
|
|
271
|
+
* are dropped, first occurrence wins. Distinct certificates that share a key
|
|
272
|
+
* (e.g. a re-issued root) are both kept, so neither validity window is lost.
|
|
233
273
|
*/
|
|
234
274
|
declare class CompositeTrustStore implements TrustStore {
|
|
235
275
|
private readonly stores;
|
|
@@ -291,7 +331,9 @@ interface PresentationResult {
|
|
|
291
331
|
interface ParseOptions {
|
|
292
332
|
/**
|
|
293
333
|
* DER-encoded issuer leaf certificates used for byte-equality trust check.
|
|
294
|
-
* Kept for 0.4.0 compatibility.
|
|
334
|
+
* Kept for 0.4.0 compatibility. No chain building: a credential signed by a
|
|
335
|
+
* document signer issued under a listed root (e.g. an IACA) is rejected on
|
|
336
|
+
* this path — use `trustStore` for that.
|
|
295
337
|
* @deprecated since 0.5.0. Use `trustStore: new StaticTrustStore([...])`
|
|
296
338
|
* with root/intermediate CAs for RFC 5280 chain validation. Scheduled
|
|
297
339
|
* for removal in 1.0.0.
|
|
@@ -332,6 +374,15 @@ interface ParseOptions {
|
|
|
332
374
|
* Trust anchor resolver. When provided, the library performs RFC 5280
|
|
333
375
|
* chain validation and ignores `trustedCertificates`. When unset, the
|
|
334
376
|
* library falls back to 0.4.0 byte-equality against `trustedCertificates`.
|
|
377
|
+
*
|
|
378
|
+
* The credential's signer certificate (`x5c[0]` / `x5chain[0]`) is trusted
|
|
379
|
+
* when it either IS an anchor (byte-identical DER) or chains to one through
|
|
380
|
+
* verified signatures, using the remaining `x5c` / `x5chain` entries as
|
|
381
|
+
* untrusted path candidates. Anchors may be roots such as an ISO 18013-5
|
|
382
|
+
* IACA, or the document-signer certificate itself. For `mso_mdoc`, built
|
|
383
|
+
* chains must also satisfy the ISO 18013-5 Annex B certificate profile
|
|
384
|
+
* (DS EKU `1.0.18013.5.1.2`, mandatory keyUsage). Every certificate must be
|
|
385
|
+
* valid now and at the credential's issuance time (MSO `signed` / `iat`).
|
|
335
386
|
*/
|
|
336
387
|
trustStore?: TrustStore;
|
|
337
388
|
/**
|
|
@@ -339,7 +390,13 @@ interface ParseOptions {
|
|
|
339
390
|
* ship in 0.5.0 workstream A.2 — passing them today throws.
|
|
340
391
|
*/
|
|
341
392
|
revocationPolicy?: 'skip' | 'prefer' | 'require';
|
|
342
|
-
/**
|
|
393
|
+
/**
|
|
394
|
+
* HTTP transport for CRL/OCSP/LOTL fetches. Defaults to a guarded fetcher
|
|
395
|
+
* (`createGuardedFetcher({ allowHttp: true })`): no private/loopback/metadata
|
|
396
|
+
* targets, manual re-validated redirects (max 3), 15 s timeout, 16 MiB cap.
|
|
397
|
+
* A caller-supplied fetcher replaces those guards entirely — wrap it with
|
|
398
|
+
* `createGuardedFetcher({ fetch: yourFetcher })` to keep them.
|
|
399
|
+
*/
|
|
343
400
|
fetcher?: Fetcher;
|
|
344
401
|
/** Cache for CRL/OCSP/LOTL artefacts. Defaults to `new InMemoryCache()`. */
|
|
345
402
|
cache?: Cache;
|
|
@@ -366,6 +423,15 @@ interface ParseOptions {
|
|
|
366
423
|
* secure path.
|
|
367
424
|
*/
|
|
368
425
|
trustedIssuerJwks?: JsonWebKey[];
|
|
426
|
+
/**
|
|
427
|
+
* Accept the legacy `vc+sd-jwt` media type in the SD-JWT VC issuer JWT's
|
|
428
|
+
* `typ` header, in addition to `dc+sd-jwt`. Default `false`.
|
|
429
|
+
*
|
|
430
|
+
* @deprecated draft-ietf-oauth-sd-jwt-vc-19 removed `vc+sd-jwt`; only
|
|
431
|
+
* `dc+sd-jwt` is conformant. This exists solely to keep interoperating
|
|
432
|
+
* with wallets that have not migrated yet and will be removed in 1.0.0.
|
|
433
|
+
*/
|
|
434
|
+
allowLegacyVcSdJwtTyp?: boolean;
|
|
369
435
|
}
|
|
370
436
|
interface ICredentialParser {
|
|
371
437
|
readonly format: CredentialFormat;
|
|
@@ -574,7 +640,7 @@ declare class MalformedCredentialError extends Error {
|
|
|
574
640
|
declare class NonceValidationError extends Error {
|
|
575
641
|
constructor(message?: string);
|
|
576
642
|
}
|
|
577
|
-
type HaipValidationCode = 'EMPTY_QUERY' | 'UNSUPPORTED_FORMAT' | 'MISSING_SDJWT_META' | 'MISSING_MDOC_META' | 'NO_CLAIMS' | 'CLAIM_SETS_DISALLOWED' | 'CREDENTIAL_SETS_DISALLOWED';
|
|
643
|
+
type HaipValidationCode = 'EMPTY_QUERY' | 'UNSUPPORTED_FORMAT' | 'MISSING_SDJWT_META' | 'MISSING_MDOC_META' | 'NO_CLAIMS' | 'CLAIM_SETS_DISALLOWED' | 'CREDENTIAL_SETS_DISALLOWED' | 'EMPTY_OPTIONS' | 'DUPLICATE_CREDENTIAL_ID' | 'MISSING_CREDENTIAL_SETS' | 'UNKNOWN_OPTION_REFERENCE';
|
|
578
644
|
declare class HaipValidationError extends Error {
|
|
579
645
|
readonly code: HaipValidationCode;
|
|
580
646
|
readonly credentialId?: string;
|
|
@@ -589,7 +655,7 @@ declare abstract class OpenID4VPError extends Error {
|
|
|
589
655
|
declare class TrustAnchorNotFoundError extends OpenID4VPError {
|
|
590
656
|
readonly code: "trust_anchor_not_found";
|
|
591
657
|
}
|
|
592
|
-
type ChainErrorReason = 'signature' | 'validity' | 'name_constraints' | 'key_usage' | 'basic_constraints' | 'path_length' | 'algorithm_disallowed' | 'aki_ski_mismatch';
|
|
658
|
+
type ChainErrorReason = 'signature' | 'validity' | 'name_constraints' | 'key_usage' | 'extended_key_usage' | 'basic_constraints' | 'path_length' | 'algorithm_disallowed' | 'aki_ski_mismatch';
|
|
593
659
|
declare class CertificateChainError extends OpenID4VPError {
|
|
594
660
|
readonly code: "chain_invalid";
|
|
595
661
|
readonly reason: ChainErrorReason;
|
|
@@ -624,6 +690,19 @@ declare class LotlFetchError extends OpenID4VPError {
|
|
|
624
690
|
declare class LotlSignatureError extends OpenID4VPError {
|
|
625
691
|
readonly code: "lotl_signature_invalid";
|
|
626
692
|
}
|
|
693
|
+
/**
|
|
694
|
+
* The environment is not configured to verify trusted-list signatures at all —
|
|
695
|
+
* distinct from a list whose signature did not verify.
|
|
696
|
+
*
|
|
697
|
+
* `xmldsigjs` needs a WebCrypto engine registered once per process, and this
|
|
698
|
+
* library deliberately leaves that to the consumer rather than picking a
|
|
699
|
+
* provider on their behalf. Without it, no trusted list can ever verify, so
|
|
700
|
+
* this is raised eagerly instead of degrading per-list: a configuration fault
|
|
701
|
+
* reported as a trust failure sends people bisecting valid XML.
|
|
702
|
+
*/
|
|
703
|
+
declare class LotlConfigurationError extends OpenID4VPError {
|
|
704
|
+
readonly code: "lotl_configuration_invalid";
|
|
705
|
+
}
|
|
627
706
|
type SignedRequestBuildErrorCode = 'empty_cert_chain' | 'hostname_cert_mismatch' | 'signing_key_cert_mismatch' | 'missing_encryption_jwk' | 'missing_encryption_alg' | 'missing_vp_formats' | 'unsupported_signing_alg' | 'empty_supported_enc_values' | 'missing_hostname' | 'self_signed_leaf';
|
|
628
707
|
declare class SignedRequestBuildError extends OpenID4VPError {
|
|
629
708
|
readonly code: SignedRequestBuildErrorCode;
|
|
@@ -655,6 +734,84 @@ declare class MultipleCredentialsNotSupportedError extends OpenID4VPError {
|
|
|
655
734
|
readonly presentationCount: number;
|
|
656
735
|
constructor(entryCount: number, presentationCount: number);
|
|
657
736
|
}
|
|
737
|
+
type GuardedFetchRejectReason = 'invalid_url' | 'insecure_scheme' | 'private_address' | 'dns_resolution_failed' | 'too_many_redirects' | 'invalid_redirect' | 'response_too_large' | 'timeout';
|
|
738
|
+
/**
|
|
739
|
+
* A request was refused (or cut off) by the guarded fetcher before an
|
|
740
|
+
* untrusted endpoint could be reached or could exhaust resources.
|
|
741
|
+
*/
|
|
742
|
+
declare class GuardedFetchError extends OpenID4VPError {
|
|
743
|
+
readonly code: "guarded_fetch_rejected";
|
|
744
|
+
readonly reason: GuardedFetchRejectReason;
|
|
745
|
+
readonly url: string;
|
|
746
|
+
constructor(reason: GuardedFetchRejectReason, message: string, options: {
|
|
747
|
+
url: string;
|
|
748
|
+
cause?: Error;
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Resolves a hostname to every IP address it currently maps to. Must return
|
|
754
|
+
* all records (A and AAAA): the guard rejects the host if ANY of them is
|
|
755
|
+
* non-public, and treats an empty result as a resolution failure.
|
|
756
|
+
*/
|
|
757
|
+
type HostLookup = (hostname: string) => Promise<readonly string[]>;
|
|
758
|
+
interface GuardedFetcherOptions {
|
|
759
|
+
/**
|
|
760
|
+
* Underlying transport. Defaults to `globalThis.fetch`, resolved at call
|
|
761
|
+
* time. Must honour `redirect: 'manual'` and expose the `Location` header
|
|
762
|
+
* of 3xx responses (Node's fetch/undici does; browsers return an opaque
|
|
763
|
+
* redirect, which the guard rejects).
|
|
764
|
+
*/
|
|
765
|
+
fetch?: Fetcher;
|
|
766
|
+
/**
|
|
767
|
+
* DNS resolver used for the private-address check. Defaults to
|
|
768
|
+
* `node:dns` `lookup(host, { all: true })` when available. In runtimes
|
|
769
|
+
* without `node:dns` (browsers, some edge workers) only IP-literal and
|
|
770
|
+
* `localhost` targets are checked — inject a resolver there if needed.
|
|
771
|
+
*/
|
|
772
|
+
lookup?: HostLookup;
|
|
773
|
+
/** Permit `http:` targets. Default `false` (SD-JWT VC draft-19: HTTPS only). */
|
|
774
|
+
allowHttp?: boolean;
|
|
775
|
+
/** Permit loopback / private / link-local / metadata targets. Default `false`. */
|
|
776
|
+
allowPrivateNetworks?: boolean;
|
|
777
|
+
/** Maximum redirects followed; every hop is re-validated. Default 3. */
|
|
778
|
+
maxRedirects?: number;
|
|
779
|
+
/** Wall-clock budget for the whole exchange (DNS, redirects, body). Default 15 000 ms. */
|
|
780
|
+
timeoutMs?: number;
|
|
781
|
+
/** Maximum response body size, enforced while streaming. Default 16 MiB. */
|
|
782
|
+
maxResponseBytes?: number;
|
|
783
|
+
}
|
|
784
|
+
declare const GUARDED_FETCH_DEFAULTS: {
|
|
785
|
+
readonly maxRedirects: 3;
|
|
786
|
+
readonly timeoutMs: 15000;
|
|
787
|
+
readonly maxResponseBytes: number;
|
|
788
|
+
};
|
|
789
|
+
/**
|
|
790
|
+
* Builds a {@link Fetcher} that applies the HTTP-retrieval rules of
|
|
791
|
+
* draft-ietf-oauth-sd-jwt-vc-19 to every request:
|
|
792
|
+
*
|
|
793
|
+
* - HTTPS only (unless `allowHttp`), and never an https → http downgrade
|
|
794
|
+
* - no loopback / private / link-local / cloud-metadata targets, checked
|
|
795
|
+
* on the URL host AND every address it resolves to (IPv4, IPv6,
|
|
796
|
+
* IPv4-mapped/compatible, NAT64, 6to4)
|
|
797
|
+
* - redirects handled manually, bounded, and re-validated hop by hop
|
|
798
|
+
* - a single timeout covering DNS, every hop and the body
|
|
799
|
+
* - a response-size cap enforced while streaming
|
|
800
|
+
*
|
|
801
|
+
* **DNS rebinding.** The guard resolves the host, validates the addresses,
|
|
802
|
+
* then hands the *hostname* to the transport, which resolves it again. An
|
|
803
|
+
* attacker-controlled DNS server can answer differently the second time
|
|
804
|
+
* (TOCTOU). Closing that gap requires pinning the validated address at the
|
|
805
|
+
* socket layer, which `fetch` does not expose portably. Deployments that
|
|
806
|
+
* dereference attacker-influenced URLs should additionally route egress
|
|
807
|
+
* through a filtering proxy or inject a transport whose connector validates
|
|
808
|
+
* the connected address (e.g. an undici `Agent` with a checking
|
|
809
|
+
* `connect.lookup`).
|
|
810
|
+
*
|
|
811
|
+
* Caching is out of scope here: the trust module caches CRL/OCSP/LOTL
|
|
812
|
+
* artefacts through its own {@link Cache} plug.
|
|
813
|
+
*/
|
|
814
|
+
declare function createGuardedFetcher(options?: GuardedFetcherOptions): Fetcher;
|
|
658
815
|
|
|
659
816
|
interface LotlTrustStoreOptions {
|
|
660
817
|
/** Override the bundled signing anchors. Empty is rejected. */
|
|
@@ -693,6 +850,6 @@ declare class LotlTrustStore implements TrustStore {
|
|
|
693
850
|
private refresh;
|
|
694
851
|
}
|
|
695
852
|
|
|
696
|
-
declare const VERSION = "0.
|
|
853
|
+
declare const VERSION = "0.13.0";
|
|
697
854
|
|
|
698
|
-
export { type AuthorizationRequest, type AuthorizationRequestInput, type AuthorizationResponse, type Cache, CertificateChainError, type ChainErrorReason, CompositeTrustStore, type CredentialClaims, type CredentialFormat, DecryptionFailedError, type EncryptedResponse, ExpiredCredentialError, type Fetcher, HAIP_DOCTYPE_NAMESPACES, type HaipQueryInput, type HaipValidationCode, HaipValidationError, type ICredentialParser, InMemoryCache, InvalidSignatureError, type IssuerInfo, type LotlAnchorMetadata, LotlFetchError, LotlSignatureError, LotlTrustStore, type LotlTrustStoreOptions, MalformedCredentialError, MdocParser, MissingDecryptionKeyError, MissingVerifierEncryptionKeyError, MultipleCredentialsNotSupportedError, NonceValidationError, OpenID4VPError, type ParseOptions, type PresentationResult, RevocationCheckFailedError, RevokedCertificateError, SdJwtParser, type SignedAuthorizationRequest, type SignedAuthorizationRequestInput, SignedRequestBuildError, type SignedRequestBuildErrorCode, StaticTrustStore, type TrustAnchor, TrustAnchorNotFoundError, type TrustStore, type TrustStoreHint, type TrustStoreInput, UnsupportedFormatError, UnsupportedJweError, VERSION, type VerifyAuthorizationResponseOptions, type VerifyOptions, type VerifyResult, buildHaipQuery, buildOid4vpSessionTranscript, buildOpenID4VPHandoverSessionTranscript, createAuthorizationRequest, createSignedAuthorizationRequest, decryptAuthorizationResponse, isHaipQuery, parsePresentation, validateHaipQuery, verifyAuthorizationResponse, verifyPresentation };
|
|
855
|
+
export { type AuthorizationRequest, type AuthorizationRequestInput, type AuthorizationResponse, type Cache, CertificateChainError, type ChainErrorReason, CompositeTrustStore, type CredentialClaims, type CredentialFormat, type CredentialSetQueryInput, DecryptionFailedError, type EncryptedResponse, ExpiredCredentialError, type Fetcher, GUARDED_FETCH_DEFAULTS, GuardedFetchError, type GuardedFetchRejectReason, type GuardedFetcherOptions, HAIP_DOCTYPE_NAMESPACES, type HaipQueryInput, type HaipValidationCode, HaipValidationError, type HostLookup, type ICredentialParser, InMemoryCache, InvalidSignatureError, type IssuerInfo, type LotlAnchorMetadata, LotlConfigurationError, LotlFetchError, LotlSignatureError, LotlTrustStore, type LotlTrustStoreOptions, MalformedCredentialError, MdocParser, MissingDecryptionKeyError, MissingVerifierEncryptionKeyError, MultipleCredentialsNotSupportedError, NonceValidationError, OpenID4VPError, type ParseOptions, type PresentationResult, RevocationCheckFailedError, RevokedCertificateError, SdJwtParser, type SignedAuthorizationRequest, type SignedAuthorizationRequestInput, SignedRequestBuildError, type SignedRequestBuildErrorCode, StaticTrustStore, type TrustAnchor, TrustAnchorNotFoundError, type TrustStore, type TrustStoreHint, type TrustStoreInput, UnsupportedFormatError, UnsupportedJweError, VERSION, type VerifyAuthorizationResponseOptions, type VerifyOptions, type VerifyResult, buildCredentialSetQuery, buildHaipQuery, buildOid4vpSessionTranscript, buildOpenID4VPHandoverSessionTranscript, createAuthorizationRequest, createGuardedFetcher, createSignedAuthorizationRequest, decryptAuthorizationResponse, isHaipQuery, parsePresentation, validateCredentialSetQuery, validateHaipQuery, verifyAuthorizationResponse, verifyPresentation };
|