Two CVEs published on 3 October 2026 show how an OpenID Connect server can get PKCE and client metadata wrong: OpenAM before 16.1.3 only enforced PKCE for the plain code response type, and it fetched a client’s jwks_uri without validating it. Keycloak’s source and documentation handle both cases explicitly, but a few client settings decide whether your realm is protected, and this post walks through them.
Key Takeaways
- In 2026, VulnCheck reported that OpenAM before 16.1.3 enforced PKCE only when
response_typewas exactlycode(CVE-2026-105119), and that an unvalidatedjwks_uriallowed SSRF (CVE-2026-105122).- Keycloak checks PKCE for hybrid flows and rejects a code verifier that has no matching challenge.
- Set the client PKCE method to
S256, disable flows you do not use, and restrict client URIs with a client policy.
What are CVE-2026-105119 and CVE-2026-105122?
Both are OpenAM vulnerabilities, published on 2026-10-03 by VulnCheck and listed in the National Vulnerability Database (NVD) with the status “Deferred”, which means NVD is not prioritizing its own enrichment of the records (the CVE data itself comes from VulnCheck). The CVE records give a public disclosure date of 2026-09-18. Both say OpenAM before 16.1.3 is affected, and both CVE records list 16.1.3 as the first unaffected version (NVD, CVE-2026-105119 and NVD, CVE-2026-105122, 2026).
The first, CVE-2026-105119, is a PKCE enforcement bypass. VulnCheck scored it 7.6 (High) on CVSS 4.0, with a weakness classification of CWE-285 (improper authorization). The second, CVE-2026-105122, is server-side request forgery (SSRF) through jwks_uri, scored 5.3 (Medium) and classified as CWE-918. The scores come from VulnCheck, not from NVD.
The records also reference GitHub security advisories GHSA-5p2f-7vcr-6vfh and GHSA-g7cv-hh35-cc7c in the OpenIdentityPlatform/OpenAM repository. The technical descriptions below come from the VulnCheck advisories and the NVD text.
How did the OpenAM PKCE bypass work?
According to the VulnCheck advisory, OpenAM applied PKCE enforcement only to authorization requests whose response_type was exactly code. Codes issued through the OpenID Connect hybrid flows (code token, code id_token and code token id_token) carried no bound challenge, so an attacker who intercepted such a code could redeem it for a public client’s tokens with any non-empty code_verifier (VulnCheck, “OpenAM before 16.1.3 PKCE Enforcement Bypass via OAuth 2.0 Hybrid Flows”, 2026).
To see why that matters, recall what PKCE in OAuth (Proof Key for Code Exchange, RFC 7636) is for. A public client, such as a single page app or a mobile app, cannot keep a secret, so an attacker who steals the authorization code from a redirect could otherwise exchange it for tokens. With PKCE the client sends a hash of a random secret (the code challenge) with the authorization request and reveals the secret (the code verifier) only when it redeems the code, so a stolen code alone is useless.
The OpenAM flaw has two parts: the challenge was never stored for hybrid requests, and the token endpoint accepted a verifier anyway. The IETF’s OAuth 2.0 Security Best Current Practice (RFC 9700, January 2025) addresses the second part directly, saying authorization servers must accept a token request containing a code_verifier only if a code_challenge was present in the authorization request (IETF, RFC 9700, “Best Current Practice for OAuth 2.0 Security”, January 2025). That rule is called PKCE downgrade protection.
Does Keycloak enforce PKCE on hybrid flows?
Yes, based on the current Keycloak source code. In the Keycloak repository, the authorization endpoint check checkPKCEParams returns early only for pure implicit flows, with a code comment that PKCE applies to flows using an authorization code, “Hybrid Flow” included. The helper isImplicitFlow is true only when the response type contains token or id_token and does not contain code, so code id_token goes through the PKCE checks (Keycloak, AuthorizationEndpointChecker.java and OIDCResponseType.java, main branch, retrieved 2026-10-18).
The token endpoint has the downgrade protection too. For a client without a configured PKCE method, Keycloak’s checkParamsForPkceNotEnforcedClient rejects a request that contains a code_verifier when the authorization request had no challenge, with the message “PKCE code verifier specified but challenge not present in authorization”. It also rejects a missing verifier when a challenge was stored, and verifies the verifier against the stored challenge otherwise (Keycloak, PkceUtils.java, main branch, retrieved 2026-10-18). Keycloak therefore does not accept an arbitrary verifier the way the OpenAM advisory describes.
There is still a gap that applies to Keycloak and to most other servers: enforcement is optional unless you configure it. The Keycloak server administration guide says that with the PKCE method left blank, Keycloak does not apply PKCE unless the client sends the PKCE parameters, so PKCE is possible but not required (Keycloak, Server Administration Guide, current documentation, retrieved 2026-10-18).
How do you require PKCE for Keycloak OIDC clients?
Where the setting lives depends on your Keycloak version, which we checked in the admin console source for each release. From 26.6.0 the client’s Settings tab has a “Require PKCE” switch under Capability config, and turning it on sets the method to S256. From 26.3.0 to 26.5.x the same Capability config section has a “PKCE Method” dropdown, where you pick S256. In 26.2 and earlier it is “Proof Key for Code Exchange Code Challenge Method” on the Advanced tab. In every version, once the method is configured the authorization endpoint rejects requests that do not use it. We cover the click path in our guide to creating a PKCE authorization flow client in Keycloak, and the React API access example shows the client side.
Choose S256 rather than plain. The administration guide lists plain as an option, but RFC 9700 notes that S256 is currently the only method that does not expose the verifier in the authorization request, so with plain an attacker who can read that request can defeat PKCE.
To apply this across a realm instead of client by client, Keycloak has a client policy executor named PKCE Enforcer. Its source rejects authorization requests with a missing or non-S256 code challenge method and can set S256 automatically on clients it covers. That automatic setting runs only when a client is registered or updated, and only if the executor’s auto-configure option is on. An existing client keeps a blank method until it is saved again (with auto-configure off, the save is rejected until you set S256 yourself), although the executor still enforces S256 on its authorization requests (Keycloak, PKCEEnforcerExecutor.java, main branch, retrieved 2026-10-18).
Two related settings reduce the exposure the OpenAM bug depended on. First, the “Implicit flow” switch on a client controls both implicit and hybrid response types: the authorization endpoint refuses them when the switch is off. Turn it off for every client that does not need it. Second, the Keycloak client policy documentation lists an executor that prohibits implicit and hybrid flows for clients in scope of a policy.
You can also test the behavior on a staging realm. Use a test client with the Implicit flow switch on, send a hybrid request such as response_type=code id_token with a nonce and a well formed S256 challenge computed from verifier A, and use the fragment response mode, because Keycloak refuses response_mode=query for hybrid response types. Then redeem the code with a different verifier B and confirm you get an invalid_grant error. Running this after upgrades takes a few minutes and covers the hybrid case OpenAM missed.
What is the JWKS SSRF in CVE-2026-105122?
A client in OpenID Connect can publish its public signing keys at a jwks_uri, a URL the server downloads to verify things like signed client assertions. VulnCheck says OpenAM before 16.1.3 did not validate that URL, so an attacker able to register or modify an OAuth 2.0 client could make OpenAM fetch internal resources, and that those fetches could then be triggered by unauthenticated requests, because OpenAM downloads the jwks_uri while it validates client authentication and ID tokens. The listed outcomes are probing internal hosts, metadata endpoints or local files, and exhausting request threads for denial of service (VulnCheck, “OpenAM before 16.1.3 SSRF via OpenID Connect Client jwks_uri”, 2026).
SSRF means the attacker does not reach the internal network directly but persuades your server to send requests on their behalf. A cloud metadata endpoint, which many cloud platforms expose on an internal address, is the typical target because the server can reach it and the attacker cannot.
Does Keycloak have the same JWKS URI risk?
Keycloak’s documentation treats it as a risk class to configure against. The server administration guide has a section on mitigating SSRF that names the client JWKS URI as a field that triggers requests from the server, and says that without strict validation a malicious administrator, a compromised account or Dynamic Client Registration could point it at internal resources or loopback addresses (Keycloak, Server Administration Guide, “Mitigating Server-Side Request Forgery (SSRF)”, retrieved 2026-10-18). We found no current Keycloak CVE matching the OpenAM ones, but there is a precedent: CVE-2020-10770 described Keycloak before 13.0.0 being forced to call an unverified URL through the OIDC request_uri parameter, also an SSRF (NVD, CVE-2020-10770, 2020).
The mitigation is a client policy. The Secure Client URIs executor enforces HTTPS and no wildcards on client URIs, and its source already checked the JWKS URI in the 22.0.0 tag, but it does not restrict which hosts are allowed. The newer Secure Client URIs Pattern executor does that by validating URIs against an allowlist of regular expressions, and the documentation says that if the list is empty or invalid the executor blocks all URIs. Going by the repository tags, that executor first appears in 26.4.11, 26.5.6 and 26.6.0, and is absent from 26.4.10 and 26.5.5, so on an older patch release you only have the basic executor.
The documentation’s own example for restricting to trusted domains is ^https://([a-zA-Z0-9-]+.)?mycompany.com(/.*)?$, which rejects localhost and numeric IP addresses. Because a hostname that passes a pattern can still resolve to an internal address, the same guide also has a DNS rebinding section that you should read alongside it.
How do you lock down Dynamic Client Registration in Keycloak?
Dynamic Client Registration lets software create clients through an API instead of an administrator doing it in the console, so it is a trust boundary, and whoever can register a client controls fields like jwks_uri and redirect URIs. The Keycloak client registration documentation recommends initial access tokens, which can only create clients and have a configurable expiry and a limit on how many clients they can create. Anonymous registration is possible, but then you need Client Registration Policies (Keycloak, “Client Registration”, securing apps documentation, retrieved 2026-10-18).
That same page notes that Client Registration Policies apply only to requests through the Client Registration Service, not to arbitrary Admin REST API requests, and that the current plan is to replace them with Client Policies. For SSRF in particular it points to client policies that restrict URIs such as the JWKS to trusted patterns or domains. In practice that means:
- Prefer initial access tokens with a short expiry and a low client limit, and avoid anonymous registration where you can.
- Allowlist the hosts your clients may use for
jwks_uriwith the Pattern executor, and apply the URI executors to the registration path as well as to console edits, since both can set it. - Review who holds admin and client-management roles, because the same documentation lists a compromised account as an SSRF path.
- Watch for clients whose
jwks_urior redirect URIs are outside your expected domains.
For checking a legitimate endpoint after you restrict URIs, our JWKS verifier fetches a JWKS URL or takes pasted keys and verifies a token signature in your browser. For the resource server side of the same problem, see our post on CVE-2026-5430 and JWT algorithm confusion, and for binding tokens to clients, see NIST IR 8587 token protection.
What should teams moving off OpenAM plan for?
If you run OpenAM, upgrade to 16.1.3 or later first, since both advisories give that as the fixed version. If you are evaluating a replacement such as managed Keycloak, treat the move as a chance to inventory every OIDC client: its response types, whether it is public or confidential, how it authenticates, and whether it registers a jwks_uri. The first CVE turned on the response type and client type, and the second on who can register or modify clients and the jwks_uri they supply, and all four map to Keycloak settings you can set deliberately from the start.
Keycloak has no importer for OpenAM client configuration that we know of, so plan to recreate clients, either by hand or by scripting the Admin REST API, and then apply the PKCE and URI policies before any traffic moves. Our identity provider exit plan post covers the inventory and sequencing.
If you would rather not own the patching, identity management as a service moves that work to the provider, while the client settings above stay your team’s responsibility. Keep your realm on a current Keycloak release, and read the Keycloak release notes for the line you run, because the executors above only exist from the versions noted.
Frequently Asked Questions
Is Keycloak affected by CVE-2026-105119 or CVE-2026-105122?
Both CVE records name OpenAM from the OpenIdentityPlatform project, with versions before 16.1.3 affected, and neither names Keycloak. Keycloak’s source rejects a verifier without a stored challenge and applies PKCE checks to hybrid flows. Keycloak’s documentation separately describes jwks_uri SSRF as a risk to restrict with client policies.
Should I use S256 or plain for PKCE?
Use S256. RFC 9700 (IETF, January 2025) notes that S256 is currently the only challenge method that does not expose the verifier in the authorization request, and the Keycloak PKCE Enforcer executor accepts only S256. The plain method remains available in the client setting for legacy clients.
Does PKCE protect confidential clients too?
RFC 9700 says its PKCE advice applies to all kinds of OAuth clients, including web applications, not only native apps. Keycloak lets you set the PKCE method on any client, so you can require S256 for confidential clients as well as public ones.
What is the difference between Dynamic Client Registration and Client Policies in Keycloak?
Dynamic Client Registration is the API that creates clients. Client Registration Policies run only on that API, while Client Policies are the more general mechanism and can validate client settings on creation and update. Keycloak’s documentation says the older registration policies are planned to be replaced by Client Policies.
How do I check that my realm blocks a PKCE downgrade?
On a staging realm, use a client with no PKCE method configured, start an authorization request without a code challenge, then redeem the code at the token endpoint with an arbitrary code_verifier. Keycloak should answer with invalid_grant. For the hybrid case, repeat the test with response_type=code id_token, the Implicit flow switch on, a nonce, and the fragment response mode.
What to do next
Set the PKCE method to S256 on every public client, turn off the Implicit flow switch where it is not needed, and add a client policy that restricts jwks_uri and the other client URIs to domains you trust. Then review who can register clients dynamically, and if you still run OpenAM, upgrade to 16.1.3 or later.
Sources
- VulnCheck, “OpenAM before 16.1.3 PKCE Enforcement Bypass via OAuth 2.0 Hybrid Flows”, retrieved 2026-10-18, https://www.vulncheck.com/advisories/openam-before-16.1.3-pkce-enforcement-bypass-via-oauth-2.0-hybrid-flows
- VulnCheck, “OpenAM before 16.1.3 SSRF via OpenID Connect Client jwks_uri”, retrieved 2026-10-18, https://www.vulncheck.com/advisories/openam-before-16.1.3-ssrf-via-openid-connect-client-jwks-uri
- NIST, National Vulnerability Database records for CVE-2026-105119 and CVE-2026-105122, retrieved 2026-10-18, https://nvd.nist.gov/vuln/detail/CVE-2026-105119 and https://nvd.nist.gov/vuln/detail/CVE-2026-105122
- CVE Program, CVE-2026-105122 record (affected versions), retrieved 2026-10-18, https://www.cve.org/CVERecord?id=CVE-2026-105122
- IETF, RFC 9700, “Best Current Practice for OAuth 2.0 Security”, January 2025, retrieved 2026-10-18, https://www.rfc-editor.org/rfc/rfc9700.html
- Keycloak, Server Administration Guide (client settings, Client Policies, “Mitigating Server-Side Request Forgery (SSRF)”), retrieved 2026-10-18, https://www.keycloak.org/docs/latest/server_admin/
- Keycloak, “Client Registration”, securing apps documentation, retrieved 2026-10-18, https://www.keycloak.org/securing-apps/client-registration
- Keycloak, source files AuthorizationEndpointChecker.java, OIDCResponseType.java, PkceUtils.java, PKCEEnforcerExecutor.java and SecureClientUrisExecutor.java, main branch, plus the 22.0.0, 26.2.0, 26.3.0, 26.4.10, 26.4.11, 26.5.5, 26.5.6 and 26.6.0 tags, retrieved 2026-10-18, https://github.com/keycloak/keycloak
- NIST, National Vulnerability Database record for CVE-2020-10770, retrieved 2026-10-18, https://nvd.nist.gov/vuln/detail/CVE-2020-10770