1.2.x

1.2.0

Compare: https://github.com/walt-id/waltid-identity-enterprise/compare/v1.1.1...v1.2.0Release date: 2026-10-14 (UTC)

Highlights

  • Added OpenID4VCI key attestation on Issuer2. A credential configuration can accept a key attestation inside a JWT proof, or a standalone attestation proof, and can require one. Trust is a public JWK, an X.509 CA, or a KMS verification key.
  • Added Wallet2 batch issuance. One receive call can select offered credential configurations and request several copies, each bound to its own holder key. A single binding remains the default.
  • Added per-issuer mandatory PAR. Set requirePushedAuthorizationRequests to require the pushed authorization endpoint. The default stays false.
  • Let one signedMetadata configuration sign Credential Issuer metadata and OAuth authorization server metadata, with a JWK or an x5c certificate chain. Omitting it still signs with tokenKeyId.
  • Added maintenance mode for an organization or tenant. It is off unless the maintenance feature is enabled.
  • Tightened credential status: Token Status List ttl is configurable, Verifier2 caches a list for that lifetime, and a status update after rollover uses the list that holds the index.

Features

Issuer2 key attestation

  • JWT proofs can carry key_attestation in the protected header. The holder signature is checked, the attestation is checked against issuer trust, and the proof key must be one of the attested keys.
  • Standalone proofs.attestation is a key-attestation JWT. It uses the same keyAttestationConfig and does not replace OAuth client attestation.
  • Set key_attestations_required on the JWT proof type to require an attestation. Leave it unset to keep attestation optional.
  • keyAttestationConfig.verificationMethod accepts static-jwk, x509-chain, or key-reference.

Wallet2 batch issuance

  • POST /v2/{target}/wallet-service-api/credentials/receive/resolve-offer/batch returns the offer and the issuer batchSize.
  • Pre-authorized and authorization-code receive, and the isolated REST steps, accept credentials with explicit holder bindings.
  • Copies of one target go out as one Credential Request with several proofs.jwt. Different configurations or datasets stay separate requests.
  • Omitting credentials still receives every granted target once. Deferred results and partial progress are unchanged.

Mandatory Pushed Authorization Requests

  • Service create and configuration update accept requirePushedAuthorizationRequests. Discovery advertises it as require_pushed_authorization_requests.
  • When it is true, the wallet posts the authorization parameters to the pushed authorization endpoint, then opens the authorization endpoint with client_id and request_uri. A direct authorization request is invalid_request.
  • The default is false, so existing issuers still allow both direct authorization and PAR.
  • External login uses the authorization request that PAR accepted.

Signed issuer and authorization-server metadata

  • signedMetadata selects the signing key for Credential Issuer metadata: inline JWK, inline PEM key plus certificate chain, or a KMS key reference. A KMS reference can attach x5c certificates from the X.509 store.
  • The same object signs OAuth authorization server metadata.
  • When signedMetadata is omitted, Issuer2 keeps signing with tokenKeyId and publishes the public jwk.
  • Clients still choose JSON or JWT with Accept.

Maintenance mode

  • Enable the maintenance feature, then restart. With the feature off, the routes are not registered.
  • POST /v1/{target}/organization-management-api/maintenance/enable freezes writes for an organization, or for one tenant when the target is that tenant. The default window is 1 day and the maximum is 30 days. Send enabledFor or enabledUntil, not both.
  • Writes from accounts that lack organization-management-maintenance-override are refused. The all permission includes that override, so a default organization admin keeps writing until the override is denied on their role.
  • Ending maintenance is allowed during the window. Other pods pick up the change within 5 seconds.

Credential status lifetime and rollover

  • IETF Token Status List configuration accepts ttl in seconds. The published token uses that value, and S3, Azure Blob, and Google Cloud Storage objects send Cache-Control: public, max-age=<ttl>. Omitted ttl stays 43200 (12 hours).
  • Verifier2 reuses a downloaded status list until Cache-Control: max-age elapses.
  • Creating a status entry returns statusListRef, the list that holds the new index. After automatic rollover that can be a versioned list. Later index updates must use that list. Session updates can still name the original configuration.
  • Enterprise issuance strips statusListRef out of credential data before signing.

Licensing

  • A license can keep a chosen credential lifetime. Online licenses are 4 to 14 days. Offline licenses are 1 to 365 days. The same interval is used for the first credential and each renewal. Leaving it blank keeps the server default of 4 days.
  • The license admin UI can unbind an installation. Unbinding lets another deployment activate the license. An offline bundle stays valid until it expires, and cumulative usage totals reset. License type can be edited only while no installation is bound.

Default requests

  • A stored default is applied when the target is an entry under the service, such as a certificate id on X.509 create or upsert. Previously only a target that was the service itself picked up the default.
  • A Wallet2 receive default may store the holder key and DID without an offer. The offer is still required on the receive call.

Administration

  • Listing tenant accounts checks that the tenant path is inside the authenticated organization.
  • The admin organization list honors the advertised pagination parameters and rejects invalid ones.
  • Organization and tenant configuration updates validate organization ids and DID registry references and return the documented errors.

Fixes and improvements

  • Wallet2 pre-authorized receive on DocumentDB no longer fails with a storage error after the issuer has already returned the credential.
  • Concurrent receives into one Wallet2 wallet retry a contended credential write instead of failing the issuance that already succeeded.
  • A presentation is signed with the holder key the credential was issued to. A different key is rejected before it is sent.
  • Revoking or reading a status index after auto-rollover updates the list that holds that index, not the base list that allocated earlier entries.
  • Verifier2 accepts an external IETF status list whose x5c leaf matches the credential. Lists hosted by this deployment stay additionally allowed.
  • Issuer2 credential responses, including failures before the response writer, send Cache-Control: no-store.
  • Deleting an organization also removes its host aliases, role entries on accounts, events, and Issuer2 OAuth state. The accounts remain. The eIDAS audit chain is kept.
  • Assigning a role to a user or API key requires every permission that role grants. Removing a deny rule requires the permission it withheld. Removing a grant does not.
  • Wallet2 receive defaults clientId to the same wallet client id the wallet library uses, so an authorization-code exchange no longer fails with a client mismatch.
  • Issuer2 OpenAPI examples match the request bodies the API accepts.
  • The Enterprise UI updates Vue to 3.5.42 and source-map-js to 1.2.2 for the advisory in the Vue server renderer.
  • Database profile fields that the API never read are marked unused, and a migration scheduling bug found while documenting PostgreSQL is fixed. Profile behavior is otherwise unchanged.

Breaking changes

None

Last updated on October 14, 2026