Disabled but not denied: a Keycloak JWT grant that ignores the off switch
The single most important button in any identity system is the one that turns an account off. Offboarding a departing employee, cutting off a compromised account, revoking a contractor: all of it comes down to setting a user to disabled and trusting that every path into the system honors that state. A flaw in Keycloak, tracked as CVE-2026-1609, breaks that trust in one specific path. When the JWT authorization grant preview feature is enabled, Keycloak fails to check whether a user is disabled before issuing them a token. The off switch is flipped, and the door still opens.
What the flaw is
Keycloak supports a JWT authorization grant: a flow where a caller presents a signed assertion token, typically issued by an external identity provider that Keycloak trusts, and exchanges it for a Keycloak access token. It is a normal federation pattern. The bug is that, in the affected versions with this preview feature enabled, the grant processing does not validate the user's disabled status. A remote attacker with low privileges can present a valid assertion token from an external IdP for an account that has been disabled in Keycloak, and Keycloak will still mint a JWT for that disabled user. With that token in hand, the attacker reaches the resources the account was supposed to have lost access to.
Read the shape of it carefully, because it is a textbook broken-access-control pattern: one authentication path enforces a security state, another path forgets to. The password login flow presumably checks disabled status. The JWT authorization grant flow does not. An account that cannot log in the front door walks in the side door and comes out with the same keys.
Why this class of bug is so dangerous
Disabled-account bypasses are quietly among the worst identity findings, and the reason is timing. The accounts most likely to be in a disabled state are exactly the accounts you most want to be sure are dead: the employee who just left, sometimes on bad terms; the service account you killed after a suspected compromise; the contractor whose engagement ended. Every one of those is a plausible, motivated attacker who may still hold, or be able to obtain, a valid external assertion. If disabling the account did not actually revoke access through every grant type, then your offboarding process, your incident response containment, and your access reviews are all resting on an assumption that is false in one path.
It is also dangerous because it is invisible in the obvious place. An administrator who disables a user and then confirms they can no longer log in with a password will see exactly what they expect. The bypass lives in a different flow that the administrator never tests by hand. Nothing looks broken, which is precisely why it survives until someone goes looking.
The exposure conditions, stated plainly
Two conditions gate this, and both are worth checking against your own deployment rather than assuming:
First, the JWT authorization grant is a preview feature, so it has to be enabled. Preview features are opt-in, which narrows exposure, but organizations doing token exchange and federation enable exactly these kinds of features to make their SSO topology work. Do not assume it is off; confirm it.
Second, the attacker needs a valid assertion token from an external IdP that Keycloak trusts, for the disabled user. In a federated environment where the external IdP is the system of record for authentication, a disabled-in-Keycloak account may still be perfectly able to authenticate at the upstream IdP and produce that assertion, especially if the disable action was taken in Keycloak but not mirrored upstream. That gap between "disabled here" and "still valid there" is the crack this bug widens into a breach.
How to tell if you are exposed
Check your Keycloak version against the fixed release and check whether the JWT authorization grant preview feature is enabled in any realm. If it is enabled, treat the deployment as vulnerable until patched. Then do the test the bug relies on you never doing: take a disabled test account that can still produce a valid external assertion, run it through the JWT authorization grant, and see whether Keycloak issues a token. If it does, you have just reproduced the finding in your own environment. In your logs, look for token issuance events tied to accounts whose status is disabled, which should never occur and is the cleanest signal that this path is being used.
What to do today
Patch Keycloak to the fixed release. If you cannot patch in this change window, disable the JWT authorization grant preview feature until you can, since a preview feature that mints tokens for disabled users is not worth keeping live. Independently, close the federation gap: make sure that disabling an account propagates to the upstream identity provider, or that Keycloak treats the local disabled flag as authoritative on every grant path, so a valid upstream assertion cannot resurrect a locally killed account. After patching, review recent token issuance for any disabled accounts and treat any hits as a potential access event, not just a bug.
The general lesson
Every security state your identity system can hold, disabled, locked, password-expired, MFA-required, must be enforced on every path that issues credentials, not just the one path you test by hand. Federation multiplies the number of those paths, and each new grant type is another place the check can be forgotten. We test identity systems by exercising the off switch through every enabled flow, because "they cannot log in" is not the same statement as "they cannot get a token."
Reference
CELVEX Group tracks this issue in the scanner catalog and tests disabled-account enforcement across enabled grant types, not only the interactive login. Refer to the vendor advisory for the affected versions and fixed release.