Keycloak 26.8.0, tagged on 1 October 2026, ships an OpenID4VP verifier, so a Keycloak realm can now accept a credential presentation from a digital wallet as a way to log in. It is experimental: the oid4vc-vp feature is disabled by default, is not intended for production, and may change or be removed in a later release. The verifier is built as an identity provider, accepts a single SD-JWT VC per presentation, takes its request as a DCQL query, and supports the direct_post and direct_post.jwt response modes. For an EUDI relying party, 26.8 is the release to start testing against, while the verifier you run in production today stays where it is.
Our September post on acting as an EUDI wallet relying party with Keycloak said OpenID4VP was absent from every release up to 26.7.3. That was accurate when we wrote it, and it stopped being accurate this week. This post covers what landed, what the source code shows that the release notes leave out, and a checklist for moving toward the native verifier. The regulation and deadline material in the earlier post still stands, so we only restate the dates here rather than repeat the whole regulatory FAQ.
What changed for OpenID4VP between Keycloak 26.7 and 26.8?
The verifier moved from the main branch into a release. The Keycloak 26.8.0 release notes, published in October 2026, add a section titled “Verify credentials with OID4VP (experimental)”, which says Keycloak “can now act as an OID4VP verifier, enabling authentication flows where users present verifiable credentials from their wallets.” In 26.7.5, the newest 26.7 patch at the time of writing, neither the provider class nor its feature flag exists.
Here is the before and after for the pieces an EUDI relying party cares about:
| Keycloak 26.7.5 | Keycloak 26.8.0 | |
|---|---|---|
| OpenID4VP verifier | Not present, no feature flag | Present as an identity provider behind oid4vc-vp |
| Verifier maturity | Not applicable | Experimental, disabled by default |
| OID4VCI issuance | Experimental | Preview, via oid4vc-vci or --features=preview |
| mdoc credential format | Not in the release | New experimental feature oid4vc-mdoc |
| Admin guide chapter for the verifier | None | None, only release notes and source |
What does experimental status mean for the oid4vc-vp feature?
Experimental is the lowest rung of Keycloak’s feature maturity ladder, below preview and supported. Profile.java at the 26.8.0 tag declares the feature as OID4VC_VP("Support for the OID4VP protocol as part of OID4VC.", Type.EXPERIMENTAL). In practice that means it is off unless you enable it, it is not meant for production traffic, and its configuration or behaviour can change, or the whole feature can be removed, in a later release.
One operational detail is easy to miss: --features=preview enables only preview features, and the verifier is experimental, so you have to name it explicitly. If you also want issuance, list both:
# Verifier only
bin/kc.sh start --features=oid4vc-vp
# Verifier plus OID4VCI issuance
bin/kc.sh start --features=oid4vc-vci,oid4vc-vp
The second detail is documentation. The 26.8.0 documentation tree has no server administration guide chapter for the verifier, only the release notes paragraph. Everything in this post about configuration keys comes from reading the source at the 26.8.0 tag. That is normal for an experimental feature, but it means you are learning the configuration from Java classes, and we think that is a bigger practical barrier to adoption right now than the experimental label itself.
How does the Keycloak OpenID4VP verifier work?
Keycloak implements the verifier as an identity broker type called OID4VPIdentityProvider, so a wallet sits in your realm the same way Google or an upstream OIDC provider does. The class javadoc at the 26.8.0 tag says it “Supports the same device and cross device flows with a single SD-JWT VC”, and it comes with an admin console page for adding the provider and a login theme template, login-oid4vp.ftl.
The release notes only mention cross-device presentation, but the login page in the source handles both cases. It renders an openid4vp:// link that opens a wallet installed on the same device, and a QR code for a wallet on a different device, typically a phone held up to a laptop screen. In the cross-device flow the browser polls a status endpoint until Keycloak has received and checked the wallet’s response. In the same-device flow there is no polling: Keycloak returns a redirect URI in its reply to the wallet’s direct_post, and the wallet sends the browser back to Keycloak to finish the login.
On the protocol side, three choices are visible in the source:
- Signed requests with
x509_hash. Keycloak signs its request object with the private key of a realm ES256 key and identifies itself with thex509_hashclient identifier prefix. The client identifier is a SHA-256 hash of that key’s X.509 certificate, so the wallet can check the request really came from the holder of that certificate. - Two response modes. With
direct_post, the wallet sends its response with an HTTP POST straight to a Keycloak endpoint instead of redirecting back through the browser. Withdirect_post.jwt, that same response is encrypted to an ephemeral key Keycloak generates for the request, which the release notes call out specifically. - One credential, one format. The verifier accepts a single SD-JWT VC per presentation. SD-JWT VC is the JWT-based credential format with selective disclosure, which lets the wallet reveal only the claims you asked for.
Once Keycloak has verified the presentation, the result travels through the same brokering machinery as any other identity provider, including the first broker login flow, account linking and identity provider mappers. 26.8 adds two mappers for this: the SD-JWT Attribute Importer copies a presented claim onto the user record, and the SD-JWT User Session Attribute Importer attaches it to the login session instead. To get a session attribute into tokens, pair it with a User Session Note protocol mapper on the client or client scope, and note that multivalued selections are stored joined with commas.
Which configuration options does the OpenID4VP identity provider expose?
These are the configuration keys in the provider at the 26.8.0 tag. Treat the names as the current source rather than a documented contract.
| Key | What it controls |
|---|---|
dcqlQuery |
The DCQL query sent to the wallet, describing which credential and claims you want |
trustedIssuerJwks |
Issuer keys the verifier trusts when checking the credential signature |
trustMaterialIdps |
Aliases of other identity providers whose trust material the verifier reuses |
principalAttribute |
The top-level scalar claim used as the brokered user’s id and username |
walletScheme |
The URL scheme for the same-device link, defaulting to openid4vp:// |
signingKeyId |
The realm ES256 key that signs the request object; it must carry an X.509 certificate |
responseMode |
direct_post (the default when blank) or direct_post.jwt |
The trustMaterialIdps option matches the release notes line that “trust material for credential verification can be delegated to an external identity provider by alias.” It lets you keep issuer trust configuration in one place, for example in the Default Trust identity provider type, and point several verifiers at it, rather than pasting the same issuer keys into each provider. The provider refuses to save unless one of trustedIssuerJwks or trustMaterialIdps is set.
What does a DCQL query look like?
DCQL, the Digital Credentials Query Language, is the JSON query format OpenID4VP 1.0 defines for a verifier to say which credentials and claims it wants. Keycloak uses DCQL here rather than the older Presentation Exchange format. A query asking for a name and birth date from one SD-JWT VC looks roughly like this, following the shape in the OpenID Foundation specification:
{
"credentials": [
{
"id": "identity",
"format": "dc+sd-jwt",
"meta": { "vct_values": ["https://credentials.example.com/identity_credential"] },
"claims": [
{ "path": ["given_name"] },
{ "path": ["family_name"] },
{ "path": ["birthdate"] }
]
}
]
}
The credential type value is a placeholder, and you should take the real one from the issuer whose credential you accept. The shape matches the DCQL query in Keycloak’s own OpenID4VP conformance test configuration at the 26.8.0 tag. Because the verifier handles a single credential, keep the credentials array to one entry. The source carries TODO notes about inferring the DCQL query from identity provider mappers, so this hand-written field may change.
What did Keycloak 26.8 change on the issuance side?
OID4VCI, the issuance protocol, moved from experimental to preview, enabled with --features=oid4vc-vci or --features=preview. According to the 26.8.0 release notes, it gains revocation when refresh tokens are revoked, an Application Initiated Action that lets users request issuance within an authenticated session, and key attestation configuration with x5c certificate validation hardened along the lines of the HAIP profile.
The same notes point to integration guides for the Lissi and Valera wallets, add the experimental oid4vc-mdoc feature for the mdoc format, and keep pre-authorized code, the REST credential offer endpoint and attestation-based client authentication experimental. Our OID4VCI introduction explains the issuer, holder and verifier roles if you need the background, though it was written before this promotion.
The 26.8.0 upgrading guide also changes when an email counts as verified: only a completed email verification marks it, and the guide names the OID4VCI credential offer emails and identity provider account linking by email among the affected behaviours. If you issue credentials by email offer, read that section before upgrading.
What is still missing for an EUDI relying party?
Quite a lot is still missing, which is expected for a first experimental release. The verifier handles one SD-JWT VC per presentation, so a request combining credentials from several issuers is out of scope. The oid4vc-mdoc feature appears under issuance in the release notes, and the verifier’s javadoc mentions SD-JWT VC only, so do not assume the verifier accepts mdoc presentations.
The most important gap for security is that the DCQL query is not enforced on what comes back. In the 26.8.0 source, OID4VPIdentityProviderEndpoint.java carries a TODO to “bind the presentation to the requested credential type and claims, so that not any credential from a trusted issuer satisfies the login.” Today the verifier checks issuer trust, the signature, key binding and the presence of the principal claim, so any credential from an issuer you trust that carries that claim will log a user in. Until that changes, your trust configuration is your whole acceptance policy: trust only the issuers and keys you mean to accept, and where the trust material exposes X.509 anchors, the verifier requires an x5c certificate chain that validates against them.
The source follows the HAIP profile in specific places, such as a fresh ephemeral encryption key per request for direct_post.jwt and certificate chain validation, and Keycloak runs the OpenID Foundation’s OpenID4VP 1.0 verifier conformance test plan against it in its own test tree. Neither the release notes nor the source claim full HAIP conformance, a conformance certificate, or any EUDI certification. The x509_hash prefix is also the only client identifier type so far, with a TODO for others such as x509_san_dns. Relying party registration also stays outside Keycloak, because under the regulation it happens with a national registrar.
The timeline leaves room for this to mature. As our earlier post set out, Regulation (EU) 2024/1183 requires Member States to make wallets available by 24 December 2026, and the obligation on the regulated private sector to accept them follows on 24 December 2027, both counted from the entry into force of Commission Implementing Regulation (EU) 2024/2977 and its companion acts. Nobody has said when the verifier will reach preview, so plan for an interim setup that lasts into 2027.
How do you move from an interim verifier to the native identity provider?
The fact that the verifier is an identity provider is what makes migration manageable. If your interim setup is a dedicated verifier brokered into Keycloak as an OIDC provider, or a community plugin, your attribute mapping should already live in Keycloak’s identity provider mappers. In that case the swap is mostly configuration: add the OpenID4VP provider, recreate the mappers so they write the same user and session attributes, and leave downstream token claims and client configuration alone. Our post on attribute mapping during identity brokering covers how those mappers behave.
The part that needs a decision is user identity. Keycloak stores a federated identity link per identity provider alias, so a user linked to your interim provider is not automatically linked to the new one. On their first wallet login through the native provider, the first broker login flow runs and either matches them to an existing account or creates a new one. Set principalAttribute to the same claim your interim setup used as the user identifier. It has to be a top-level scalar claim, and it becomes both the brokered id and the username, so choose it carefully: Keycloak’s conformance configuration uses given_name for the EUDI PID precisely because that credential carries no stable subject claim, and an unstable principal means users can be re-created across logins. Decide in advance whether linking should happen automatically or need confirmation. The email verification change in the 26.8 upgrading guide affects linking by email, so test that path with care.
We would be direct about the production question: keep the interim verifier serving real users for now. Run the native provider in a non-production realm in parallel, compare the claims both produce for the same wallet, and switch once the feature reaches at least preview and has documentation you can rely on.
What should an EUDI relying party check before upgrading to 26.8?
Upgrading to 26.8 is worth doing for evaluation, and you do not need to rush production onto it for this feature. Keycloak keeps shipping point releases on older minor lines (26.4.15 and 26.6.6 both landed on 11 August 2026, after 26.7.0, as our 26.7.3 patch checklist notes), so staying on a patched 26.7.x in production while you test 26.8 is a reasonable split. Our Keycloak cluster upgrade strategy covers the mechanics.
- Read the 26.8.0 upgrading guide in full, including the email verification change that affects identity provider linking by email and OID4VCI offer emails.
- Stand up a non-production realm on 26.8.0 with
--features=oid4vc-vp, plusoid4vc-vciif you also issue credentials. - Add an ES256 signing key with a certificate, ideally a CA-issued leaf certificate your target wallets trust (for example through a Java keystore key provider), and pin its key ID in
signingKeyId. The verifier fails unless it finds an ES256 key with a certificate: the realm’s active ES256 key whensigningKeyIdis blank, or the pinned key, which may be passive or disabled. In 26.8.0 it also accepts only ES256-signed credentials. - Rewrite your current request as a DCQL query for a single SD-JWT VC, and compare it claim by claim with what your interim verifier asks for.
- Configure trust narrowly with
trustedIssuerJwks, or withtrustMaterialIdpsif another provider already holds the issuer keys, remembering that trust is your acceptance policy while the DCQL query is not enforced. - Set
responseModetodirect_post.jwtexplicitly, because a blank value defaults to unencrypteddirect_post, unless a wallet you must support cannot handle it. - Pick
principalAttributeand the first broker login behaviour before any real user logs in through the new provider. - Test both flows with real wallets, the same-device link and the cross-device QR code, and check that a custom login theme still renders the new wallet login page.
- Pin the version and re-test after each upgrade while the feature is experimental, because configuration keys can change without notice.
Where should wallet attributes be processed?
Wallet attributes are processed wherever the realm runs, because Keycloak verifies the presentation itself. If your data-residency policy requires EU processing for EU residents, that means running the realm in an EU region. A wallet presentation carries personal data about the holder, and Keycloak processes it during verification even if you store nothing afterwards. Moving the verifier into Keycloak simplifies this, because there is no longer a separate verification service that could end up deployed elsewhere.
The two new mappers give you a data-minimisation choice. The SD-JWT User Session Attribute Importer keeps a claim on the session rather than writing it permanently onto the user, which suits attributes such as age checks that you need for one login. Anything you do persist becomes part of the PII inventory covered in our Keycloak GDPR erasure and retention guide, and the regional hosting side is discussed in cloud identity management.
Where does managed Keycloak fit?
Skycloak is identity management as a service built on upstream Keycloak, so the verifier we can offer is the same experimental verifier upstream ships, with the same limitations. What changes with a managed setup is who handles the version tracking, the feature flag on a non-production cluster, and the re-testing each time the experimental feature moves. Whether that trade is worth it depends on your team, and we went through the arithmetic in is self-hosting Keycloak worth it in 2026, with the wider market in managed Keycloak providers compared.
FAQ
Does Keycloak support OpenID4VP?
Yes, from Keycloak 26.8.0, released in October 2026, as an experimental feature called oid4vc-vp. It is disabled by default and not intended for production. The verifier works as an identity provider, accepts a single SD-JWT VC per presentation, uses DCQL queries, and supports direct_post and direct_post.jwt response modes. No 26.7.x release, up to and including 26.7.5, includes it.
Does --features=preview enable the OpenID4VP verifier?
No, that option turns on preview features only, and in 26.8 the verifier is experimental rather than preview. You have to enable it by name with --features=oid4vc-vp. OID4VCI issuance is now preview, so --features=preview does turn on issuance, which can confuse teams who expect both halves to follow the same flag.
Is the Keycloak verifier ready for EUDI wallet production use?
Not yet, because experimental features can change or be removed between releases, the 26.8.0 verifier does not yet enforce the DCQL query against the presented credential, there is no administration guide chapter for it, and neither the release notes nor the source claim full HAIP conformance or EUDI certification. Keep your current verifier in production and run the native one in a test realm, so you are ready to switch when it matures.
Can the Keycloak verifier accept mdoc credentials?
Do not plan on it. The verifier’s source describes support for a single SD-JWT VC, and the experimental oid4vc-mdoc feature appears in the 26.8 release notes as part of the issuance work. If your use case depends on mdoc presentations, such as mobile driving licences, you will need another verifier for that part for now.
Will moving from a community plugin to the native verifier break my token claims?
It should not, if your attribute mapping lives in Keycloak identity provider mappers. Recreate the mappers on the new provider so they write the same attributes, and downstream claims stay the same. The thing to plan is account linking, because links are stored per provider alias, so users go through first broker login once on the new provider.
Sources
- Keycloak, Keycloak 26.8.0 release notes (source at tag 26.8.0), https://github.com/keycloak/keycloak/blob/26.8.0/docs/documentation/release_notes/topics/26_8_0.adoc (published version: https://www.keycloak.org/docs/latest/release_notes/index.html)
- Keycloak, Upgrading Guide, changes in 26.8.0 (source at tag 26.8.0), https://github.com/keycloak/keycloak/blob/26.8.0/docs/documentation/upgrading/topics/changes/changes-26_8_0.adoc
- Keycloak, OpenID4VP identity provider source at tag 26.8.0, https://github.com/keycloak/keycloak/tree/26.8.0/services/src/main/java/org/keycloak/broker/oid4vp
- Keycloak, OID4VPIdentityProviderEndpoint.java at tag 26.8.0, https://github.com/keycloak/keycloak/blob/26.8.0/services/src/main/java/org/keycloak/broker/oid4vp/OID4VPIdentityProviderEndpoint.java
- Keycloak, VpConformanceRealmConfig.java (OpenID4VP conformance test configuration) at tag 26.8.0, https://github.com/keycloak/keycloak/blob/26.8.0/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VpConformanceRealmConfig.java
- Keycloak, Profile.java at tags 26.8.0 and 26.7.5, https://github.com/keycloak/keycloak/blob/26.8.0/common/src/main/java/org/keycloak/common/Profile.java and https://github.com/keycloak/keycloak/blob/26.7.5/common/src/main/java/org/keycloak/common/Profile.java
- OpenID Foundation, OpenID for Verifiable Presentations 1.0, 2025, https://openid.net/specs/openid-4-verifiable-presentations-1_0.html
- OpenID Foundation, OpenID for Verifiable Credential Issuance 1.0, 2025, https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html
- European Union, Regulation (EU) 2024/1183 on the European Digital Identity Framework, Articles 5a and 5f, https://eur-lex.europa.eu/eli/reg/2024/1183/oj
- European Union, Commission Implementing Regulation (EU) 2024/2977, https://eur-lex.europa.eu/legal-content/EN/ALL/?uri=CELEX%3A32024R2977