Feb 10, 2026·4 min read·34 visits
Keycloak fails to check if an Identity Provider is disabled before accepting its JWTs. If an admin disables a compromised IdP but doesn't delete its keys, the attacker can still login. CVSS 8.8.
A critical logic flaw in Keycloak's implementation of JWT Authorization Grants allows disabled Identity Providers (IdPs) to continue issuing valid access tokens. By failing to check the 'enabled' status of an IdP during the resolution process, Keycloak permits attackers possessing a disabled provider's signing keys to bypass administrative restrictions and maintain unauthorized access.
Identity and Access Management (IAM) is the bouncer of your digital club. You trust it to know who's on the list and who's banned. In Keycloak, the 'Identity Provider' (IdP) toggle is supposed to be the ultimate kill switch. You suspect a partner is compromised? You flip the switch to Disabled. You go home. You sleep tight.
But in CVE-2026-1486, that switch is a lie. It’s like locking your front door but leaving the key under the mat—and telling the burglar where the mat is. This vulnerability reveals a disconnect between the administrative intent (UI configuration) and the runtime logic (token issuance). It turns out, Keycloak was perfectly happy to mint fresh access tokens for Identity Providers that were explicitly told to go away, provided they still had the right cryptographic handshake.
The vulnerability resides in how Keycloak handles the JWT Profile for OAuth 2.0 Client Authentication and Authorization Grants (RFC 7523). When Keycloak receives a JWT assertion, it needs to figure out who signed it. It looks at the iss (issuer) claim in the token and matches it against its configured Identity Providers.
Here is the logic flow in the vulnerable versions:
iss claim.IdentityProviderModel that matches this alias/issuer.IdentityProviderModel.isEnabled() is true.The code was so focused on cryptographic verification—"Does this signature match the public key we have on file?"—that it forgot the administrative verification—"Are we even supposed to be talking to this guy?" This is a classic case of valid crypto lulling developers into a false sense of security.
The fix is embarrassingly simple, which highlights just how critical the oversight was. The patch was applied primarily in FederatedJWTClientAuthenticator.java and JWTAuthorizationGrantType.java.
Let's look at the diff in FederatedJWTClientAuthenticator.java (commit 176dc89):
// THE VULNERABLE CODE
if (lookup == null || lookup.identityProviderModel() == null || lookup.clientModel() == null) {
// Error handling
}
// THE PATCHED CODE
if (lookup == null ||
lookup.identityProviderModel() == null ||
!lookup.identityProviderModel().isEnabled() || // <--- The Savior
lookup.clientModel() == null) {
// Error handling
}And similarly in JWTAuthorizationGrantType.java:
// Added explicitly before signature validation
if (!identityProviderModel.isEnabled()) {
throw new RuntimeException("Identity Provider is not enabled");
}Without these lines, the code retrieved the configuration, saw the public keys were valid, and proceeded to grant access. The isEnabled() flag was effectively a UI decoration with no backend enforcement during this specific grant flow.
Let's construct a realistic attack scenario. Imagine you are a SaaS provider using Keycloak. You have a B2B integration with ShadyCorp, set up as an Identity Provider. You give them a client_id and import their public keys so they can mint tokens for their users to access your API.
Day 1: You read news that ShadyCorp got hacked. Their private signing keys are in the wild.
Day 2: You rush to your Keycloak Admin Console. You find the ShadyCorp Identity Provider. You toggle Enabled to OFF. You breathe a sigh of relief. You assume the threat is neutralized.
Day 3: The attacker, holding ShadyCorp's private key, crafts a JWT:
{
"iss": "shady-corp-alias",
"sub": "admin-user",
"aud": "your-keycloak-realm",
"exp": 1770000000
}They sign this JWT with the stolen private key. They send it to your Keycloak token endpoint:
POST /realms/{realm}/protocol/openid-connect/token
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
assertion={signed_jwt}
The Result: Keycloak looks up shady-corp-alias. It finds the config. It sees the public key. It verifies the signature. It ignores the fact that you disabled it. It issues a shiny new access token with full privileges. The attacker is in.
The primary fix is obviously to upgrade Keycloak. The patched versions (Red Hat build 26.4.9-1, 26.4-11, etc.) introduce the boolean checks ensuring isEnabled() is respected.
However, if you are stuck on a legacy version and cannot upgrade immediately, you must understand that disabling the provider is insufficient. To mitigate this without a patch, you must perform a 'cryptographic lobotomy':
If Keycloak cannot verify the signature (because the key is gone), the exploit fails, regardless of the logic bug.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Red Hat build of Keycloak Red Hat | 26.4.x < 26.4.9 | 26.4.9-1 |
Keycloak Keycloak | < Commit 176dc89 | PR #46148 |
| Attribute | Detail |
|---|---|
| CWE | CWE-287 |
| CVSS v3.1 | 8.8 (High) |
| Attack Vector | Network |
| EPSS Score | 0.00045 |
| Impact | Authorization Bypass |
| Exploit Status | PoC Available (Logic Trivial) |
| Patch Status | Available |