@openeudi/openid4vp 0.3.0 → 0.5.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/dist/index.d.cts CHANGED
@@ -1,3 +1,37 @@
1
+ import { DcqlQuery, TrustedAuthoritiesQuery, DcqlMatchResult, DcqlSubmission } from '@openeudi/dcql';
2
+ export { DcqlMatchError, DcqlMatchResult, DcqlQuery, DcqlSubmission, DcqlValidationError, DcqlValidationErrorCode } from '@openeudi/dcql';
3
+ import { X509Certificate } from '@peculiar/x509';
4
+
5
+ interface AuthorizationRequestInput {
6
+ clientId: string;
7
+ responseUri: string;
8
+ nonce: string;
9
+ state?: string;
10
+ responseMode?: 'direct_post' | 'direct_post.jwt';
11
+ }
12
+ interface AuthorizationRequest {
13
+ uri: string;
14
+ dcqlQuery: DcqlQuery;
15
+ nonce: string;
16
+ state: string;
17
+ }
18
+
19
+ declare function createAuthorizationRequest(input: AuthorizationRequestInput, query: DcqlQuery): AuthorizationRequest;
20
+
21
+ interface HaipQueryInput {
22
+ credentialId: string;
23
+ format: 'dc+sd-jwt' | 'mso_mdoc';
24
+ vctValues?: string[];
25
+ doctypeValue?: string;
26
+ claims: string[];
27
+ trustedAuthorities?: TrustedAuthoritiesQuery[];
28
+ }
29
+
30
+ declare const HAIP_DOCTYPE_NAMESPACES: Record<string, string>;
31
+ declare function buildHaipQuery(input: HaipQueryInput): DcqlQuery;
32
+ declare function validateHaipQuery(query: DcqlQuery): void;
33
+ declare function isHaipQuery(query: DcqlQuery): boolean;
34
+
1
35
  interface IssuerInfo {
2
36
  certificate: Uint8Array;
3
37
  country: string;
@@ -5,6 +39,168 @@ interface IssuerInfo {
5
39
  organization?: string;
6
40
  }
7
41
 
42
+ /**
43
+ * Content-addressed cache for CRL responses, OCSP responses, and LOTL
44
+ * snapshots. Keys are library-defined strings; values are raw bytes.
45
+ *
46
+ * Implementations MUST be safe to call concurrently.
47
+ */
48
+ interface Cache {
49
+ get(key: string): Promise<Uint8Array | null>;
50
+ set(key: string, value: Uint8Array, ttlSeconds: number): Promise<void>;
51
+ }
52
+ /**
53
+ * Default `Cache` implementation — bounded-size LRU with wall-clock TTL.
54
+ * Cache dies with the process; inject a persistent `Cache` impl for
55
+ * servers that should survive restarts.
56
+ */
57
+ declare class InMemoryCache implements Cache {
58
+ private readonly entries;
59
+ private readonly maxEntries;
60
+ constructor(opts?: {
61
+ maxEntries?: number;
62
+ });
63
+ get(key: string): Promise<Uint8Array | null>;
64
+ set(key: string, value: Uint8Array, ttlSeconds: number): Promise<void>;
65
+ }
66
+
67
+ /**
68
+ * HTTP transport plug. Defaults to `globalThis.fetch` when unset in
69
+ * `ParseOptions`. Consumers may inject a custom implementation for proxies,
70
+ * timeouts, retry policies, request tracing, or test doubles.
71
+ *
72
+ * MUST be safe to call concurrently.
73
+ */
74
+ type Fetcher = (url: string, init?: RequestInit) => Promise<Response>;
75
+
76
+ /**
77
+ * A certificate used as the root of a trust chain. `source` tracks provenance
78
+ * so consumers can distinguish static config from LOTL-derived anchors.
79
+ */
80
+ interface TrustAnchor {
81
+ readonly certificate: X509Certificate;
82
+ readonly source: 'static' | 'lotl';
83
+ readonly metadata?: LotlAnchorMetadata;
84
+ /**
85
+ * Authority identifiers this anchor stands for. Populated by
86
+ * `LotlTrustStore` from `ServiceDigitalIdentity`'s SKI; synthesized by
87
+ * `TrustEvaluator` from the anchor SKI for static stores. Used by
88
+ * `verifyPresentation` to populate `DecodedCredential.trusted_authority_ids`.
89
+ */
90
+ readonly trustedAuthorityIds?: readonly string[];
91
+ }
92
+ /**
93
+ * Metadata attached to anchors sourced from the EU LOTL (populated by
94
+ * `LotlTrustStore` in A.3 — left optional here so the shape is stable today).
95
+ */
96
+ interface LotlAnchorMetadata {
97
+ /** ISO 3166-1 alpha-2 country code of the TL scheme operator */
98
+ readonly country: string;
99
+ readonly serviceName: string;
100
+ /** e.g. `http://uri.etsi.org/TrstSvc/Svctype/CA/QC` */
101
+ readonly serviceTypeIdentifier: string;
102
+ readonly serviceStatus: string;
103
+ readonly qualified: boolean;
104
+ readonly loa?: 'substantial' | 'high';
105
+ }
106
+
107
+ /**
108
+ * Internal types for the LOTL (List of Trusted Lists) module. NOT exported
109
+ * from the package root. Consumed only by `LotlFetcher`, `LotlParser`,
110
+ * `NationalTlResolver`, `LotlTrustStore`, and `ProvenanceResolver`.
111
+ */
112
+
113
+ /**
114
+ * One `TSPService` inside a national TL, flattened with its provider's
115
+ * `TSPInformation` and resolved certificates.
116
+ */
117
+ interface TspService {
118
+ /** `TSPInformation.TSPName` of the provider that owns this service. */
119
+ readonly providerName: string;
120
+ /** ISO 3166-1 alpha-2 scheme territory the national TL belongs to. */
121
+ readonly country: string;
122
+ /** `ServiceTypeIdentifier`, e.g. `http://uri.etsi.org/TrstSvc/Svctype/CA/QC`. */
123
+ readonly serviceTypeIdentifier: string;
124
+ /** `ServiceStatus`, e.g. `http://uri.etsi.org/TrstSvc/TrustedList/Svcstatus/granted`. */
125
+ readonly serviceStatus: string;
126
+ /** `ServiceName` (first language variant — English preferred). */
127
+ readonly serviceName: string;
128
+ /** All X.509 certs in `ServiceDigitalIdentity`. */
129
+ readonly certificates: readonly X509Certificate[];
130
+ /**
131
+ * `AdditionalServiceInformation/URI` values. Used by the LoA mapping
132
+ * in `lotl-loa-mapping.ts` to derive `'substantial' | 'high'`.
133
+ */
134
+ readonly additionalServiceInformationUris: readonly string[];
135
+ }
136
+ /** One national TL, parsed + verified. */
137
+ interface NationalTlSnapshot {
138
+ readonly country: string;
139
+ readonly issueDate: Date;
140
+ readonly nextUpdate: Date | null;
141
+ readonly services: readonly TspService[];
142
+ }
143
+
144
+ /**
145
+ * Resolves trust anchors for a given leaf credential. The library calls
146
+ * `getAnchors()` once per verification after extracting the leaf's issuer
147
+ * hints (DN, Authority Key Identifier, JWT/COSE key ID).
148
+ *
149
+ * Implementations MUST be safe to call concurrently. Stores that cannot
150
+ * satisfy the hint MUST return `[]`, not throw.
151
+ */
152
+ interface TrustStore {
153
+ getAnchors(hint: TrustStoreHint): Promise<TrustAnchor[]>;
154
+ }
155
+ interface TrustStoreHint {
156
+ /** Leaf cert's Issuer DN (RFC 4514 string). */
157
+ issuer?: string;
158
+ /** Leaf cert's Authority Key Identifier extension bytes. */
159
+ aki?: Uint8Array;
160
+ /** JWT/COSE key ID, used by LOTL-backed stores. */
161
+ kid?: string;
162
+ }
163
+ type TrustStoreInput = X509Certificate | Uint8Array | string;
164
+ /**
165
+ * Trust store backed by a fixed list of certificates supplied at construction
166
+ * time. No I/O, no refresh. Suitable for development, testing, and consumers
167
+ * with a static issuer allowlist.
168
+ */
169
+ declare class StaticTrustStore implements TrustStore {
170
+ private readonly anchors;
171
+ private readonly skiIndex;
172
+ private readonly subjectIndex;
173
+ constructor(certs: Iterable<TrustStoreInput>);
174
+ getAnchors(hint: TrustStoreHint): Promise<TrustAnchor[]>;
175
+ }
176
+ /**
177
+ * Combines multiple `TrustStore` instances. Children are queried in parallel;
178
+ * results concatenate in child-order; duplicate anchors are dropped by
179
+ * Subject Key Identifier.
180
+ */
181
+ declare class CompositeTrustStore implements TrustStore {
182
+ private readonly stores;
183
+ constructor(stores: TrustStore[]);
184
+ getAnchors(hint: TrustStoreHint): Promise<TrustAnchor[]>;
185
+ getNationalTls(): Promise<readonly NationalTlSnapshot[]>;
186
+ }
187
+
188
+ interface TrustEvaluationResult {
189
+ chain: X509Certificate[];
190
+ anchor: TrustAnchor;
191
+ revocationStatus: 'good' | 'revoked' | 'unknown' | 'skipped';
192
+ revocationCheckedAt?: Date;
193
+ revokedAt?: Date;
194
+ revocationReason?: string;
195
+ trustedAuthorityIds?: readonly string[];
196
+ provenance?: {
197
+ loa?: 'substantial' | 'high';
198
+ qualified?: boolean;
199
+ country?: string;
200
+ serviceName?: string;
201
+ };
202
+ }
203
+
8
204
  type CredentialFormat = 'sd-jwt-vc' | 'mdoc';
9
205
  interface CredentialClaims {
10
206
  age_over_18?: boolean;
@@ -20,24 +216,33 @@ interface PresentationResult {
20
216
  claims: CredentialClaims;
21
217
  issuer: IssuerInfo;
22
218
  error?: string;
23
- }
24
-
25
- interface AuthorizationRequestInput {
26
- requestedAttributes: string[];
27
- acceptedFormats: CredentialFormat[];
28
- responseUri: string;
29
- nonce: string;
30
- clientId: string;
31
- state?: string;
32
- }
33
- interface AuthorizationRequest {
34
- uri: string;
35
- presentationDefinition: object;
36
- nonce: string;
37
- state: string;
219
+ /** SD-JWT type (Verifiable Credential Type URI). Populated for `sd-jwt-vc` format. */
220
+ vct?: string;
221
+ /** mDOC docType (ISO 18013-5). Populated for `mdoc` format. */
222
+ docType?: string;
223
+ /**
224
+ * mDOC claims grouped by namespace. Populated for `mdoc` format.
225
+ * DCQL claim paths address this shape: `['org.iso.18013.5.1', 'age_over_18']`.
226
+ */
227
+ namespacedClaims?: Record<string, Record<string, unknown>>;
228
+ /**
229
+ * Populated when `ParseOptions.trustStore` was provided and trust
230
+ * evaluation succeeded. Contains the validated chain, matched anchor,
231
+ * and revocation + provenance metadata (populated incrementally across
232
+ * 0.5.0 A.1/A.2/A.3). Omitted when `skipTrustCheck: true` or when
233
+ * `trustStore` was not provided.
234
+ */
235
+ trust?: TrustEvaluationResult;
38
236
  }
39
237
 
40
238
  interface ParseOptions {
239
+ /**
240
+ * DER-encoded issuer leaf certificates used for byte-equality trust check.
241
+ * Kept for 0.4.0 compatibility.
242
+ * @deprecated since 0.5.0. Use `trustStore: new StaticTrustStore([...])`
243
+ * with root/intermediate CAs for RFC 5280 chain validation. Scheduled
244
+ * for removal in 1.0.0.
245
+ */
41
246
  trustedCertificates: Uint8Array[];
42
247
  nonce: string;
43
248
  /** Expected audience for key binding JWT verification. Optional. */
@@ -46,18 +251,32 @@ interface ParseOptions {
46
251
  allowedAlgorithms?: string[];
47
252
  /**
48
253
  * Explicit opt-in to skip the trust check. When omitted or `false`,
49
- * `trustedCertificates` must be non-empty — otherwise parsing throws
50
- * `MalformedCredentialError`. Set to `true` for demo/mock environments
51
- * where no trusted issuer set is available.
254
+ * either `trustedCertificates` must be non-empty OR `trustStore` must be
255
+ * provided — otherwise parsing throws `MalformedCredentialError`.
52
256
  */
53
257
  skipTrustCheck?: boolean;
54
258
  /**
55
259
  * When set, the parsed credential's `docType` (mDOC) or `vct` (SD-JWT)
56
260
  * must equal this value — otherwise parsing throws `MalformedCredentialError`.
57
- * Defends against doc-type confusion attacks when the verifier expects a
58
- * specific credential class (e.g. `'eu.europa.ec.eudi.pid.1'`).
59
261
  */
60
262
  expectedDocType?: string;
263
+ /**
264
+ * Trust anchor resolver. When provided, the library performs RFC 5280
265
+ * chain validation and ignores `trustedCertificates`. When unset, the
266
+ * library falls back to 0.4.0 byte-equality against `trustedCertificates`.
267
+ */
268
+ trustStore?: TrustStore;
269
+ /**
270
+ * Revocation checking policy. Default `'skip'`. `'prefer'` and `'require'`
271
+ * ship in 0.5.0 workstream A.2 — passing them today throws.
272
+ */
273
+ revocationPolicy?: 'skip' | 'prefer' | 'require';
274
+ /** HTTP transport for CRL/OCSP/LOTL fetches. Defaults to `globalThis.fetch`. */
275
+ fetcher?: Fetcher;
276
+ /** Cache for CRL/OCSP/LOTL artefacts. Defaults to `new InMemoryCache()`. */
277
+ cache?: Cache;
278
+ /** Clock-skew tolerance in seconds for certificate validity checks. Default 60. */
279
+ clockSkewTolerance?: number;
61
280
  }
62
281
  interface ICredentialParser {
63
282
  readonly format: CredentialFormat;
@@ -65,6 +284,29 @@ interface ICredentialParser {
65
284
  parse(vpToken: unknown, options: ParseOptions): Promise<PresentationResult>;
66
285
  }
67
286
 
287
+ declare function parsePresentation(vpToken: unknown, options: ParseOptions): Promise<PresentationResult>;
288
+
289
+ type VerifyOptions = ParseOptions;
290
+ interface VerifyResult {
291
+ parsed: PresentationResult;
292
+ match: DcqlMatchResult;
293
+ submission: DcqlSubmission | null;
294
+ valid: boolean;
295
+ }
296
+
297
+ /**
298
+ * Parses a VP token, then matches it against a DCQL query.
299
+ *
300
+ * Cryptographic / structural parser failures still throw (via `parsePresentation`).
301
+ * Query-level mismatches are surfaced as `match.unmatched` entries so callers can
302
+ * show per-claim diagnostics.
303
+ *
304
+ * @param vpToken raw VP token (SD-JWT string or mDOC Uint8Array)
305
+ * @param query DCQL query describing the required credential(s)
306
+ * @param options parse + verify options (nonce, trusted issuers, etc.)
307
+ */
308
+ declare function verifyPresentation(vpToken: unknown, query: DcqlQuery, options: VerifyOptions): Promise<VerifyResult>;
309
+
68
310
  /**
69
311
  * SD-JWT VC credential parser.
70
312
  *
@@ -124,11 +366,94 @@ declare class MalformedCredentialError extends Error {
124
366
  declare class NonceValidationError extends Error {
125
367
  constructor(message?: string);
126
368
  }
369
+ type HaipValidationCode = 'EMPTY_QUERY' | 'UNSUPPORTED_FORMAT' | 'MISSING_SDJWT_META' | 'MISSING_MDOC_META' | 'NO_CLAIMS' | 'CLAIM_SETS_DISALLOWED' | 'CREDENTIAL_SETS_DISALLOWED';
370
+ declare class HaipValidationError extends Error {
371
+ readonly code: HaipValidationCode;
372
+ readonly credentialId?: string;
373
+ constructor(code: HaipValidationCode, message: string, credentialId?: string);
374
+ }
375
+ declare abstract class OpenID4VPError extends Error {
376
+ abstract readonly code: string;
377
+ constructor(message: string, options?: {
378
+ cause?: Error;
379
+ });
380
+ }
381
+ declare class TrustAnchorNotFoundError extends OpenID4VPError {
382
+ readonly code: "trust_anchor_not_found";
383
+ }
384
+ type ChainErrorReason = 'signature' | 'validity' | 'name_constraints' | 'key_usage' | 'basic_constraints' | 'path_length' | 'algorithm_disallowed' | 'aki_ski_mismatch';
385
+ declare class CertificateChainError extends OpenID4VPError {
386
+ readonly code: "chain_invalid";
387
+ readonly reason: ChainErrorReason;
388
+ constructor(message: string, options: {
389
+ reason: ChainErrorReason;
390
+ cause?: Error;
391
+ });
392
+ }
393
+ declare class RevokedCertificateError extends OpenID4VPError {
394
+ readonly code: "certificate_revoked";
395
+ readonly serial: string;
396
+ readonly revokedAt: Date;
397
+ readonly reason?: string;
398
+ constructor(message: string, options: {
399
+ serial: string;
400
+ revokedAt: Date;
401
+ reason?: string;
402
+ cause?: Error;
403
+ });
404
+ }
405
+ declare class RevocationCheckFailedError extends OpenID4VPError {
406
+ readonly code: "revocation_check_failed";
407
+ }
408
+ declare class LotlFetchError extends OpenID4VPError {
409
+ readonly code: "lotl_fetch_failed";
410
+ readonly url: string;
411
+ constructor(message: string, options: {
412
+ url: string;
413
+ cause?: Error;
414
+ });
415
+ }
416
+ declare class LotlSignatureError extends OpenID4VPError {
417
+ readonly code: "lotl_signature_invalid";
418
+ }
127
419
 
128
- declare function createAuthorizationRequest(input: AuthorizationRequestInput): AuthorizationRequest;
129
-
130
- declare function parsePresentation(vpToken: unknown, options: ParseOptions): Promise<PresentationResult>;
420
+ interface LotlTrustStoreOptions {
421
+ /** Override the bundled signing anchors. Empty is rejected. */
422
+ signingAnchors?: readonly X509Certificate[];
423
+ fetcher?: Fetcher;
424
+ cache?: Cache;
425
+ /** ms between snapshot refreshes. Default: 24h. */
426
+ refreshInterval?: number;
427
+ /** Override the LOTL URL. Defaults to the EC production URL. */
428
+ lotlUrl?: string;
429
+ }
430
+ /**
431
+ * Public TrustStore backed by the EU LOTL + national TLs. Loads lazily on
432
+ * the first `getAnchors()` call; subsequent calls within `refreshInterval`
433
+ * hit the in-memory snapshot. Graceful degradation: refresh failures serve
434
+ * the prior snapshot with a `console.warn`. Single-flight: concurrent
435
+ * refreshes share one in-flight promise.
436
+ */
437
+ declare class LotlTrustStore implements TrustStore {
438
+ private readonly fetcher;
439
+ private readonly resolver;
440
+ private readonly signingAnchors;
441
+ private readonly refreshInterval;
442
+ private readonly lotlUrl;
443
+ private readonly cache;
444
+ private snapshot;
445
+ private inFlight;
446
+ constructor(opts?: LotlTrustStoreOptions);
447
+ getAnchors(hint: TrustStoreHint): Promise<TrustAnchor[]>;
448
+ /**
449
+ * Internal accessor — used by `TrustEvaluator` for `ProvenanceResolver`.
450
+ * Not part of the public `TrustStore` contract.
451
+ */
452
+ getNationalTls(): Promise<readonly NationalTlSnapshot[]>;
453
+ private getSnapshot;
454
+ private refresh;
455
+ }
131
456
 
132
- declare const VERSION = "0.3.0";
457
+ declare const VERSION = "0.5.0";
133
458
 
134
- export { type AuthorizationRequest, type AuthorizationRequestInput, type CredentialClaims, type CredentialFormat, ExpiredCredentialError, type ICredentialParser, InvalidSignatureError, type IssuerInfo, MalformedCredentialError, MdocParser, NonceValidationError, type ParseOptions, type PresentationResult, SdJwtParser, UnsupportedFormatError, VERSION, createAuthorizationRequest, parsePresentation };
459
+ export { type AuthorizationRequest, type AuthorizationRequestInput, type Cache, CertificateChainError, type ChainErrorReason, CompositeTrustStore, type CredentialClaims, type CredentialFormat, 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, NonceValidationError, OpenID4VPError, type ParseOptions, type PresentationResult, RevocationCheckFailedError, RevokedCertificateError, SdJwtParser, StaticTrustStore, type TrustAnchor, TrustAnchorNotFoundError, type TrustStore, type TrustStoreHint, type TrustStoreInput, UnsupportedFormatError, VERSION, type VerifyOptions, type VerifyResult, buildHaipQuery, createAuthorizationRequest, isHaipQuery, parsePresentation, validateHaipQuery, verifyPresentation };