@openeudi/openid4vp 0.12.0 → 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 +51 -3
- package/dist/index.cjs +620 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -7
- package/dist/index.d.ts +112 -7
- package/dist/index.js +621 -86
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -391,6 +391,19 @@ Both `parsePresentation` and `verifyPresentation` accept:
|
|
|
391
391
|
- `trustedCertificates` (required when `trustStore` is unset) — DER-encoded issuer leaf certificates for the 0.4.x byte-equality trust check. Deprecated since 0.5.0 — pass an empty array and supply `trustStore` for production deployments.
|
|
392
392
|
- `trustStore?` — `TrustStore` instance for full RFC 5280 chain validation (e.g. `LotlTrustStore`, `StaticTrustStore`, or `CompositeTrustStore`). When set, takes precedence over `trustedCertificates`.
|
|
393
393
|
|
|
394
|
+
The signer certificate (`x5c[0]` / `x5chain[0]`) is trusted when it **is** an anchor (byte-identical DER) or **chains to** one through verified signatures; the remaining `x5c` / `x5chain` entries are untrusted path candidates and never anchors. A list longer than 10 certificates, signer included, is rejected as malformed before any entry is parsed. Anchors may therefore be roots — such as the IACA certificates an ETSI TS 119 602 PID-providers list publishes — or the document-signer certificate itself:
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
import { StaticTrustStore } from "@openeudi/openid4vp";
|
|
398
|
+
|
|
399
|
+
// DER bytes of every certificate listed for the PID providers you accept:
|
|
400
|
+
// IACA roots and directly-listed signer certificates alike.
|
|
401
|
+
const trustStore = new StaticTrustStore(listedCertificates);
|
|
402
|
+
await parsePresentation(vpToken, { trustedCertificates: [], trustStore, nonce, mdocSessionTranscript });
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Each link's signature, validity now **and** at issuance time (MSO `signed` / SD-JWT `iat`), `basicConstraints` cA + `keyUsage` keyCertSign on every CA, `digitalSignature` on the signer, `pathLenConstraint`, `nameConstraints` and a 5-certificate maximum are enforced. For `mso_mdoc`, built chains must also follow the ISO 18013-5 Annex B profile: the document signer carries EKU `1.0.18013.5.1.2` (`reason: 'extended_key_usage'` otherwise) and `keyUsage` is mandatory. A signer that is itself the listed anchor is trusted by presence and exempt from that profile. Anchors are never matched by Subject DN alone.
|
|
406
|
+
|
|
394
407
|
> **`LotlTrustStore` requires an `xmldsigjs` crypto engine.** Trusted lists are
|
|
395
408
|
> verified as signed XML, and this library does not pick a WebCrypto provider on
|
|
396
409
|
> your behalf. Register one **once at startup**, before any trusted-list fetch:
|
|
@@ -406,7 +419,7 @@ Both `parsePresentation` and `verifyPresentation` accept:
|
|
|
406
419
|
> naming this as the cause. `StaticTrustStore` is unaffected.
|
|
407
420
|
|
|
408
421
|
- `revocationPolicy?` — `'skip'` (default) | `'prefer'` | `'require'`. Controls whether the chain validator consults OCSP / CRL.
|
|
409
|
-
- `fetcher?` — HTTP transport for CRL/OCSP/LOTL fetches. Defaults to `
|
|
422
|
+
- `fetcher?` — HTTP transport for CRL/OCSP/LOTL fetches. Defaults to a guarded fetcher — see [Network access](#network-access). A fetcher you supply **replaces** the guards; wrap it with `createGuardedFetcher({ fetch: yourFetcher })` to keep them.
|
|
410
423
|
- `cache?` — cache for CRL/OCSP/LOTL artefacts. Defaults to `new InMemoryCache()`.
|
|
411
424
|
- `clockSkewTolerance?` — seconds of slack applied to certificate validity checks. Default 60.
|
|
412
425
|
- `trustedIssuerJwks?` — opt-in alternate trust path for SD-JWT VCs whose issuer JWT lacks an `x5c` header. The library matches by `kid` (or iterates the array when no `kid` is present) and skips chain validation entirely. Intended for harness setups (e.g. OIDF conformance suite) where the wallet signs without `x5c` and the verifier knows the signing key out-of-band. **Not recommended for production verifiers** — `trustStore` is the secure path.
|
|
@@ -414,6 +427,38 @@ Both `parsePresentation` and `verifyPresentation` accept:
|
|
|
414
427
|
- `allowedAlgorithms?` — restrict signature algorithms. Defaults to `['ES256','ES384','ES512']`.
|
|
415
428
|
- `skipTrustCheck?` — skip trust checks entirely (dev/test only).
|
|
416
429
|
- `expectedDocType?` — for mDOC verification, lock the credential `docType` (or SD-JWT `vct`).
|
|
430
|
+
- `allowLegacyVcSdJwtTyp?` — **deprecated**. Also accept the legacy `vc+sd-jwt` issuer-JWT `typ`, which draft-ietf-oauth-sd-jwt-vc-19 removed. Default `false`; only for interoperating with wallets that have not migrated yet. Removed in 1.0.0.
|
|
431
|
+
|
|
432
|
+
### Network access
|
|
433
|
+
|
|
434
|
+
The library dereferences URLs only inside the trust module, and only when you opt into it:
|
|
435
|
+
|
|
436
|
+
| Fetch | When | Method |
|
|
437
|
+
|---|---|---|
|
|
438
|
+
| EU LOTL + national trusted lists | `LotlTrustStore` | GET |
|
|
439
|
+
| CRL distribution points | `revocationPolicy` `'prefer'`/`'require'` | GET |
|
|
440
|
+
| OCSP responders (AIA) | `revocationPolicy` `'prefer'`/`'require'` | POST |
|
|
441
|
+
|
|
442
|
+
It does **not** fetch SD-JWT VC issuer metadata (`/.well-known/jwt-vc-issuer`), `jwks_uri`, Type Metadata (`vct` resolution), Token Status Lists, or `x5u`. Issuer keys come from `x5c` or `trustedIssuerJwks`. **If your application dereferences any of those itself, it is responsible for applying the SD-JWT VC draft-19 HTTP retrieval rules** — `createGuardedFetcher()` (HTTPS-only by default) is exported for exactly that.
|
|
443
|
+
|
|
444
|
+
All library fetches go through `createGuardedFetcher({ allowHttp: true })` unless you pass `fetcher`. It enforces:
|
|
445
|
+
|
|
446
|
+
- `https:` only by default (`allowHttp: true` for the trust module: RFC 5280 CRLs and RFC 6960 OCSP are conventionally plain HTTP and every artefact is signature-verified); never an `https` → `http` redirect downgrade.
|
|
447
|
+
- No loopback, RFC 1918, CGNAT, link-local (incl. `169.254.169.254`), ULA (incl. `fd00:ec2::254`), multicast, documentation or reserved targets — checked on the URL host *and* on every address it resolves to, including IPv4-mapped/compatible, NAT64 and 6to4 IPv6 forms. `localhost`/`*.localhost` are refused by name. A host that resolves to no addresses at all is refused as `dns_resolution_failed` rather than treated as public.
|
|
448
|
+
- Redirects handled manually (`maxRedirects`, default 3); each target is re-validated before it is requested, and credentials headers are dropped on cross-origin hops.
|
|
449
|
+
- One timeout (`timeoutMs`, default 15 000) for DNS, all hops and the body.
|
|
450
|
+
- A size cap (`maxResponseBytes`, default 16 MiB), enforced from `Content-Length` and again while streaming.
|
|
451
|
+
|
|
452
|
+
Rejections throw `GuardedFetchError` with a `reason` (`insecure_scheme`, `private_address`, `too_many_redirects`, `response_too_large`, `timeout`, …).
|
|
453
|
+
|
|
454
|
+
```ts
|
|
455
|
+
import { createGuardedFetcher, LotlTrustStore } from "@openeudi/openid4vp";
|
|
456
|
+
|
|
457
|
+
const fetcher = createGuardedFetcher({ allowHttp: true, timeoutMs: 10_000, maxResponseBytes: 32 * 1024 * 1024 });
|
|
458
|
+
const trustStore = new LotlTrustStore({ signingAnchors, fetcher });
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
> **Limits.** DNS rebinding: the guard resolves and validates the host, then the transport resolves it *again*, so a hostile DNS server can answer differently the second time. `fetch` gives no portable way to pin the validated address. If attacker-influenced URLs reach your verifier, also route egress through a filtering proxy, or inject a `fetch` whose connector checks the connected address (e.g. an undici `Agent` with a validating `connect.lookup`). In runtimes without `node:dns` (browsers, some edge workers) only IP literals and `localhost` are checked unless you pass `lookup`. Caching is handled by the `cache` option, not the fetcher.
|
|
417
462
|
|
|
418
463
|
## Supported formats
|
|
419
464
|
|
|
@@ -421,6 +466,7 @@ Both `parsePresentation` and `verifyPresentation` accept:
|
|
|
421
466
|
|
|
422
467
|
Selective Disclosure JSON Web Token Verifiable Credentials. The token is a string in `jwt~disclosure~kb` format. The parser:
|
|
423
468
|
|
|
469
|
+
- Requires the issuer JWT `typ` to be `dc+sd-jwt` (draft-ietf-oauth-sd-jwt-vc-19); the legacy `vc+sd-jwt` is rejected unless `allowLegacyVcSdJwtTyp` is set
|
|
424
470
|
- Decodes the issuer JWT and extracts the `x5c` certificate chain
|
|
425
471
|
- Verifies the issuer certificate against your trusted set
|
|
426
472
|
- Checks credential expiry from the `exp` claim
|
|
@@ -435,7 +481,7 @@ Mobile Document credentials as defined in ISO 18013-5. The token is a CBOR-encod
|
|
|
435
481
|
|
|
436
482
|
- Decodes the CBOR DeviceResponse structure
|
|
437
483
|
- Extracts the issuer certificate from the COSE_Sign1 `issuerAuth` (x5chain label 33)
|
|
438
|
-
- Verifies the certificate against your trusted set
|
|
484
|
+
- Verifies the certificate against your trusted set — with `trustStore`, by building a chain from the document signer to an anchor such as an IACA (see [`trustStore`](#parseoptions--verifyoptions))
|
|
439
485
|
- Checks the validity period from `validityInfo`
|
|
440
486
|
- Extracts claims from the `eu.europa.ec.eudi.pid.1` namespace
|
|
441
487
|
- Verifies each `IssuerSignedItem`'s digest against the MSO's `valueDigests`, computed over the full tag-24 `IssuerSignedItemBytes` (`#6.24(bstr .cbor IssuerSignedItem)`) per ISO 18013-5 §9.1.2.4 — not just the inner CBOR — matching how real wallets encode mdocs. mDOC verification, including this digest check and device authentication below, is validated against the OIDF conformance suite acting as an independent ISO 18013-5 mdl wallet.
|
|
@@ -493,6 +539,7 @@ class MyCustomParser implements ICredentialParser {
|
|
|
493
539
|
| `NonceValidationError` | Nonce does not match expected value | Key binding JWT nonce does not match |
|
|
494
540
|
| `HaipValidationError` | HAIP query constraint violated | DCQL query fails `validateHaipQuery` |
|
|
495
541
|
| `LotlConfigurationError` | No xmldsigjs crypto engine registered | `LotlTrustStore` used without `Application.setEngine` |
|
|
542
|
+
| `GuardedFetchError` | refusing … / exceeds … / timed out | A guarded fetch is refused or cut off (`reason`) |
|
|
496
543
|
|
|
497
544
|
```ts
|
|
498
545
|
import { MalformedCredentialError, ExpiredCredentialError } from "@openeudi/openid4vp";
|
|
@@ -518,7 +565,7 @@ This library implements the **verifier side** of OpenID4VP for SD-JWT VC and mDO
|
|
|
518
565
|
- **HAIP** — `buildHaipQuery` / `validateHaipQuery` helpers for the High Assurance Interoperability Profile, plus `buildCredentialSetQuery` / `validateCredentialSetQuery` for `credential_sets` disjunctions.
|
|
519
566
|
- **Signed authorization requests (JAR)** — `createSignedAuthorizationRequest` per RFC 9101 / OpenID4VP 1.0 §5.10 with `x509_san_dns` or `x509_hash` client-id binding. Emits the OpenID4VP 1.0 Final `client_metadata` shape. Self-signed verifier leaf certificates are rejected by default per HAIP 1.0 Final.
|
|
520
567
|
- **Encrypted responses** — `decryptAuthorizationResponse` for `direct_post.jwt` (ECDH-ES + A128GCM/A256GCM), `verifyAuthorizationResponse` for the 1.0 §8.1 object-keyed `vp_token` envelope.
|
|
521
|
-
- **X.509 chain validation** — RFC 5280 chain building including `nameConstraints`, `StaticTrustStore`, `CompositeTrustStore
|
|
568
|
+
- **X.509 chain validation** — RFC 5280 chain building including `nameConstraints`, `StaticTrustStore`, `CompositeTrustStore`; IACA → document-signer chains for `mso_mdoc` with the ISO 18013-5 Annex B certificate profile.
|
|
522
569
|
- **Revocation checking** — OCSP-first with CRL fallback (`revocationPolicy: 'skip' | 'prefer' | 'require'`).
|
|
523
570
|
- **EU List of Trusted Lists** — `LotlTrustStore` resolves national trust lists via signed XML fetch + XAdES verification; populates `provenance` (LoA, qualified status, country, service name) on verified presentations.
|
|
524
571
|
- **OIDF conformance** — automated against the OpenID Foundation conformance suite in CI (`oidf-pr.yml` happy-flow gate, `oidf-release.yml` full plan).
|
|
@@ -561,6 +608,7 @@ See [CHANGELOG.md](./CHANGELOG.md) for per-release changes. Key migration moment
|
|
|
561
608
|
- **0.9.3** — `reflect-metadata` is loaded by the package itself in both ESM and CJS builds. If you added a manual `import "reflect-metadata"` before importing this library to work around the 0.9.2 bug, you can drop it.
|
|
562
609
|
- **0.11.0** — **BREAKING:** `createSignedAuthorizationRequest` now rejects a self-signed leaf certificate with `SignedRequestBuildError: self_signed_leaf`, per HAIP 1.0 Final. Verifiers using a self-signed certificate for their own identity must either move to a CA-issued certificate (the correct fix) or pass `allowSelfSignedCertificate: true` to keep the previous behaviour. Verification of wallet presentations is unaffected — this concerns only the verifier's own request-signing certificate.
|
|
563
610
|
- **0.10.0** — `createSignedAuthorizationRequest` gains `clientIdPrefix` (`x509_san_dns` default, or `x509_hash`), and `hostname` becomes optional — required only for `x509_san_dns`, but still validated against the leaf SAN whenever supplied. **Wire change for existing callers:** the emitted `client_metadata` no longer carries the singular ID3 `authorization_encrypted_response_alg` / `authorization_encrypted_response_enc` fields added in 0.8.0. Verifiers that read the 1.0 Final `encrypted_response_enc_values_supported` array (and `alg` from `client_metadata.jwks`) are unaffected; anything still reading the singular fields needs updating.
|
|
611
|
+
- **0.13.0** — **BREAKING:** SD-JWT VC issuer JWTs must carry `typ: dc+sd-jwt` (draft-ietf-oauth-sd-jwt-vc-19); `vc+sd-jwt` is rejected unless you pass `allowLegacyVcSdJwtTyp: true`, which is already deprecated and goes in 1.0.0. **BREAKING:** trust-module fetches (LOTL / national TL, CRL, OCSP) are SSRF-guarded by default — a CA, OCSP responder or trusted list on a private network now needs `fetcher: createGuardedFetcher({ allowHttp: true, allowPrivateNetworks: true })`. IACA → document-signer chain building only applies on the `trustStore` path: `trustedCertificates` is still byte-equality, so pass `trustStore: new StaticTrustStore([...])` to accept PIDs whose list publishes the IACA rather than the signer.
|
|
564
612
|
|
|
565
613
|
## License
|
|
566
614
|
|