Repository navigation
9.0.0 (2026-10-05)
- Fix OIDF FAPI2 conformance coverage (#1079) (446e875)
- fix(authorization)!: bound refresh tokens, enforce S256 for interactive authorization, honor built-in token settings (#1135) (42cfbdb), closes #1104
- fix(authorization)!: verify DPoP proofs, require PKCE for every authorization code, bind codes to their grant and verify interactive authorization (#1143) (500f50d)
- fix(issuance)!: call external and upstream authorization servers under the outbound URL policy (#1157) (def2051)
- fix(platform)!: fail closed on broken TLS configuration and clean up repository tooling (#1140) (b98cc00)
- fix(registrar)!: match registration certificate claims by path and add SKIP_* flags (#1097) (474e249)
- fix(security)!: protect key material and KMS secrets, enforce key attestation requirements and federation trust (#1146) (d5dca08)
- fix(session)!: enforce session expiry at request time and count sessions from the database (#1127) (03b2591), closes #1120 #1123 #1120 #1123
- fix(session)!: scope session access by role and fix the session event stream (#1145) (21c667e), closes #1142 #820 #820 #1141
- fix(status-list)!: reject values that do not fit the list, make revocation final and restrict status changes to issuance roles (#1142) (a83e1b5)
- fix(storage)!: replace MinIO with RustFS across deployments (#1075) (57d70a6)
- fix(trust)!: derive DCQL aki values from listed issuers and renew managed trust lists (#1134) (ed0bbe9)
- fix(trust)!: fetch trust lists, status lists, federation entities and CRLs under the outbound URL policy (#1155) (af23b1d)
- fix(verifier)!: enforce DCQL claim values in OpenID4VP presentations (#1148) (4b1d728)
- fix(verifier)!: report failed presentations to webhooks, classify mDOC failures and finish SSE streams (#1133) (f50c3a2)
Bug Fixes
- verifier: accept wallet error responses with HTTP 200 and record them on the session (#1112) (950ae62)
- storage: allow S3 credentials to be omitted for IRSA/instance-profile auth (#1073) (89088d2)
- database: bootstrap fresh schemas through migrations (#1072) (74d83f3)
- trust: bound the status list caches (#1159) (2f454a0), closes #1155
- trust: clear the federation trust cache from the cache endpoints (#1105) (5af1761)
- security: enforce outbound URL policy on schema metadata fetches and remove ReDoS-prone regex (#1109) (7a6b13b)
- issuance: enforce PKCE with S256 on the chained authorization servers (#1104) (2bf496f)
- config-import: follow symlinked tenant folders during discovery (#1107) (5cca3ed)
- improve protocol debugging logs (#1077) (b992728)
- issuance: keep mDOC signed date within the signing certificate validity (#1113) (5c8a904)
- move to the EUDIPLO organization and LF Decentralized Trust (#1161) (d3706be)
- issuance: open OID4VP wallet request via tap instead of bare redirect (#1098) (672625e)
- deployment: pin the bundled PostgreSQL to 16 in the Compose files (#1139) (3c4fbcd), closes #1129
- client: preserve array child wildcard paths during credential config nesting (#1071) (66cc9c9)
- verifier: require non-empty vp_token entries and enforce DCQL multiple (#1106) (4e47fd9)
- client: require status management for Single Active Credential and default built-in token lifetime to 300 s (#1144) (fb37ece)
- issuance: resolve notification sessions like the credential endpoint and validate activeCredentials in the API (#1138) (545119e)
- webhook: return mandatory mDL claims from the example claims webhook (#1111) (6eb6376)
- issuance: send presented credentials to the attribute provider (#1108) (0c0759d)
- database: store the session OAuth expiry columns as timestamp on PostgreSQL (#1151) (728e41e)
- issuance: treat children of array claim fields as item properties (#1099) (53974dc), closes #1080
- sdk,cli: use the /api prefix where the backend serves it and await the stored request object (#1141) (05183ed)
- client: use the logged-in instance URL after an SSO login (#1118) (e8b8571), closes #1076
- security: verify CRLs against the issuing CA before trusting them (#1156) (6401287)
- client: warn when client and backend come from different builds (#1115) (b6dd020)
Documentation
- upgrade: cover role-scoped sessions, the event stream header and the DB_SYNCHRONIZE default in the 9.0 guide (#1152) (4ab71f0), closes #1145
Features
- security: allow restricting CORS origins for the management API via CORS_ORIGINS (#1114) (bc53368), closes #1088
- cli: expand doctor with production readiness checks (#1078) (f137547)
- session: filter and search the session list and correlate logs with sessions (#1147) (7e6a44b), closes #1121
- issuance: validate resolved claims against the credential configuration schema (#1080) (4b7fd4c)
BREAKING CHANGES
- external authorization servers and upstream providers
of chained authorization servers on HTTP or on private addresses, and
their JWKS and introspection endpoints, are rejected unless
OUTBOUND_URL_ALLOW_HTTP or OUTBOUND_URL_ALLOW_PRIVATE_NETWORK is set. The
upstream token request no longer follows redirects.
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
Co-authored-by: Claude Opus 5.5 noreply@anthropic.com
- trust lists, status lists and federation entities on
HTTP or on private addresses, and CRLs on private addresses, are rejected
unless OUTBOUND_URL_ALLOW_HTTP or OUTBOUND_URL_ALLOW_PRIVATE_NETWORK is
set. URLs on EUDIPLO's own PUBLIC_URL or INTERNAL_URL are not affected.
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
Co-authored-by: Claude Opus 5.5 noreply@anthropic.com
- upgrade: changes, and it never mentioned that #1072 changed the
DB_SYNCHRONIZE default.
Upgrade guide:
- New "Clients see only the sessions of their side": session list, read,
logs and delete only cover the client's side; what the other side gets
(empty list, 404, 204 without delete) and the fix (a role of each side). - "The session event stream needs the Authorization header and ends": the
header-only token, the scope,?token=rejected with 401, the SDK 9.0
fetchclient, reading the stream from the backend. - New "Schema changes only through migrations": DB_SYNCHRONIZE defaults to
false (8.x: true); keep it out of production env files. - Intro, audience table, "After the upgrade" checklist and the summary on
the upgrade overview name the access-control and default changes; the
overview no longer says "configuration format v2" (PresentationConfig is
v3).
Env examples:
- Compose minimal/standard/full: commented OUTBOUND_URL_* block for
services reached over HTTP inside the Compose network; CORS_ORIGINS in
standard and full. - Root .env.example: SESSION_TTL, STATUS_BITS (suspension needs 2 bits) and
CORS_ORIGINS. - KMS links in the Compose and Kubernetes examples point to /operate/kms.
Also: the Compose page pairs S3_ACCESS_KEY_ID with RUSTFS_ACCESS_KEY, and
the release checklist describes the /upgrade URLs the 9.0 CLI prints.
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
Co-authored-by: Claude Opus 5.5 noreply@anthropic.com
- OpenID4VP presentations whose disclosed claim is not one
of the claim query'svaluesnow fail withclaim_value_mismatch;
valuesmust match the claim type ([true], not["true"]).
PresentationConfig exports use file format v3.
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
Co-authored-by: Claude Opus 5.5 noreply@anthropic.com
- a client with only
presentation:request(and optionally
presentation:manage) no longer sees issuance sessions; a client with only
issuance:offer(and optionallyissuance:manage) no longer sees
presentation sessions. Give a client that must read both kinds a role of
each side.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
- fix(session)!: authorize the session event stream like reading the session
GET /api/session/{id}/events read the tenant from payload.entity, which
a token from the token endpoint never contains (it carries tenant_id), so
GET /api/session/{id}/eventsrequires the access token in
theAuthorizationheader andissuance:offerorpresentation:request,
scoped likeGET /api/session/{id}; thetokenquery parameter is rejected
with 401. The browser'sEventSourcecannot send the header: read the
stream from your backend.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
- key export and the KMS provider configuration require
tenant:adminortenants:manage; KMS credentials are returned redacted;
configurations withkeyAttestationsRequiredreject proofs without a
matching key attestation; federation-only DCQL queries reject issuers
outside the federation;federation.roleother thanleafand
enforceSigningPolicy: falseare rejected.
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
Co-authored-by: Claude Opus 5.5 noreply@anthropic.com
- invalid DPoP proofs are rejected; a DPoP-bound
authorization or refresh requires a proof with the bound key; codes are
only accepted for the grant they were issued for.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
- fix(issuance)!: verify interactive authorization presentations and authenticate web completion
- The
openid4vp_presentationstep of interactive authorization only
JSON-parsed the wallet'sopenid4vp_responseand then issued an
authorization code, so any JSON completed the step. The response now
goes through the regular OID4VP verification (decryption, nonce,
audience, DCQL, trust, single use) for the request created for this
auth session; only a completed verification finishes the step.
Verified credentials are forwarded to attribute providers like for the
OID4VP authorization server, andissuer_statemust name a redeemable
authorization code offer. The verifier session is now created before
the request so wallets can fetch the request object. complete-web-authwas unauthenticated, so the wallet that knows the
auth session could complete theredirect_to_webstep itself. It now
requires anissuance:offertoken of the same tenant and only completes
an unexpired auth session whose current step isredirect_to_web.- Follow-up requests must match the current step, an auth session yields
one code, and interactive authorization codes expire after 60 s.
- interactive authorization requires S256 PKCE and the
encrypted OpenID4VP authorization response;complete-web-authrequires an
issuance:offermanagement token of the tenant; interactive authorization
codes expire after 60 s.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
- startup fails when TLS is enabled but the certificate,
key or CA file cannot be read;TLS_CA_PATHserves the intermediate chain
and does not enable mTLS.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
- test(platform): keep certificate validation on in the TLS chain test
The handshake test disabled certificate validation to inspect the served chain (CodeQL js/disabling-certificate-validation). Only the root is trusted, so with validation on the handshake fails unless the server sends the intermediate, which is what the test checks.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
- suspending (
status: 2) a credential on a status list
with 1 bit per entry is rejected with 400; use lists with 2 or more bits
(STATUS_BITS, the tenant status list config, orbitswhen creating a
list). Lists that already contain such values are no longer published
until their entries are set to a valid value.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
- docs: document the status value range check and the remediation for corrupted lists
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
- fix(status-list)!: make revocation final and restrict status changes to issuance roles
- A revoked credential can no longer be reinstated (1 -> 0) or suspended
(1 -> 2): the update is rejected with 409 before any entry changes.
Suspensions can still be lifted or turned into a revocation. The rule is
checked for every entry up front and again inside the versioned write. POST /api/session/revokerequiresissuance:offeror
issuance:manage; verifier-only clients (presentation:request) can no
longer change credential status.- Describe the endpoint correctly in the OpenAPI document (it changes the
status of a session's credentials; 400 and 409 responses). - Docs: revocation page, revocable credentials cookbook (suspension is
shown on a new credential) and the 9.0 upgrade guide.
- reinstating or suspending a revoked credential returns 409;
POST /api/session/revokerequires an issuance role.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
- chained/OID4VP servers issue 30-day refresh tokens unless
disabled, and refresh tokens without a stored expiry expire 30 days after
session creation; interactive authorization requires S256; the built-in
server applies its token lifetime, signing key and DPoP settings;
/issuers/{tenant}/chained-as-vp/*is removed.
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
Co-authored-by: Claude Opus 5.5 noreply@anthropic.com
provided_attestationsis rejected; PresentationConfig
exports use file format v2; concurrent trust list updates return 409.
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
Co-authored-by: Claude Opus 5.5 noreply@anthropic.com
- presentation webhooks also fire for failed and declined
presentations; consumers must check the newstatusfield. The SSE stream
now completes after a terminal status.
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
Co-authored-by: Claude Opus 5.5 noreply@anthropic.com
- expired or finished sessions are rejected at request time
(HTTP 400/404 or OAuthinvalid_grant);expiresAtis a full timestamp;
presentations emit afetchedstatus; thesessionsmetric is a gauge that
every replica reports, so aggregate it withmax; IssuanceConfig exports use
file format v2. - OUTBOUND_URL_ALLOW_HTTP and
OUTBOUND_URL_ALLOW_PRIVATE_NETWORK now default to false in every
environment instead of true outside NODE_ENV=production. Set them
explicitly for local development against HTTP/localhost services.
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
Co-authored-by: Claude Opus 5.5 noreply@anthropic.com
- PAR now requires a redirect_uri and S256 PKCE, and request_uri and authorization-code lifetimes are reduced to 60 seconds.
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
- chore: remove file
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
- Bundled deployments now use RustFS service names, credentials, and fresh storage volumes. Existing MinIO objects require an S3-based migration before switching endpoints; see the migration guide.
Signed-off-by: Mirko Mollik mirko.mollik@eudi.sprind.org
This release is also available on:
v9.0.0- npm package (@latest dist-tag)
v9.0.0