Skip to main content

Sign-in Methods, Grant Types and Clients

How a user signs in to Keycloak is decided by three things working together: the grant type an application uses to ask for tokens, the client settings that allow that grant, and the authentication flow Keycloak runs for it. Most security gaps we see in production come from a mismatch between the three: a protection is added to one flow, and an attacker uses a grant or a client that runs a different one.

This page lays out each piece and how they fit, so you can decide what to enable before you go live. The behavior described here was checked against Keycloak 26.8.1.

Grant types​

A grant type is the way a client asks the token endpoint for tokens. Each one is switched on per client, in the client's Settings tab under Capability config, and each one runs a different flow, or none at all.

Grant typeClient setting that enables itFlow that runsUser involvedBrowser-flow protections apply?Recommendation
Authorization code + PKCEStandard flowBrowser flowYesYes: CAPTCHA, OTP and other second factors, passkeys, identity provider redirects, required actionsThe default for every interactive login, including single-page and mobile apps. Require PKCE on public clients.
Client credentialsClient authentication on, plus Service account rolesClient authentication flow onlyNo, the client acts as itselfNot applicableThe right grant for backend services and integrations. See Service Accounts.
Direct access grant (resource owner password credentials)Direct access grantsDirect grant flowYes, the client collects the password itselfNo. Only the steps in the direct grant flow runTurn off everywhere. See below.
Refresh tokenIssued alongside the grants aboveNone. Keycloak checks the session, not the userIndirectlyNoKeep refresh token rotation on and handle concurrent refreshes. See Tokens and Sessions.
Device authorizationOAuth 2.0 Device Authorization GrantBrowser flow, on a second deviceYesYes, on the device where the user approvesOnly for input-constrained devices such as TVs and CLIs.
Token exchangeStandard Token ExchangeNone. Keycloak validates the incoming tokenIndirectlyNoOnly for services that need to call other services on a user's behalf, with a restricted audience.
Client-initiated backchannel authentication (CIBA)OIDC CIBA GrantAn external authentication device you provideYesNoRare. Only with a backchannel authentication provider in place.

Two notes on the table:

  • Client credentials never returns a refresh token. The service asks for a new access token when the old one expires. This is expected, and it means there is no long-lived session to steal.
  • A disabled grant fails cleanly. With Direct access grants off, a password request returns 400 {"error":"unauthorized_client","error_description":"Client not allowed for direct access grants"}, and the device and token exchange endpoints refuse in the same way when their switch is off.

Which flow runs​

Each realm has a binding for every flow type under Authentication, and Keycloak picks the flow from the grant:

Flow bindingDefault flowRuns for
Browser flowbrowserAuthorization code, device authorization (on the approving device)
Direct grant flowdirect grantDirect access grants
Client authentication flowclientsAny confidential client proving its identity, including client credentials

A client can replace the realm's browser or direct grant flow with its own, in the client's Advanced tab under Authentication flow overrides. An override changes that one flow for that one client and nothing else:

  • A browser flow override does not touch the direct grant flow. In testing, a client whose browser flow override was a flow that always denies access still received tokens through a direct access grant.
  • Every other client keeps the realm default. A browser flow override on your web application leaves account-console, security-admin-console and any other client on the realm's browser flow.

The per-client override trap​

Overrides are a common way to add a CAPTCHA or a second factor for the clients that matter. The trouble is that attackers do not have to use those clients. If the protected flow is bound only through overrides, every client without an override is an unprotected login page for the same users, and the built-in clients exist in every realm.

We have seen a credential stuffing campaign move between clients exactly this way: from the application's protected login, to the account console, to the realm's admin console login, and to the token endpoint through admin-cli, each time the previous path was closed.

To avoid it:

  • Bind protections at the realm level. Make the flow with your CAPTCHA and second factor the realm's browser flow, and use overrides only to make a specific client stricter, never as the only place a protection lives.
  • Turn off grants nobody uses. A flow you do not run cannot be attacked. In particular, turn off Direct access grants on every client, including admin-cli, and enforce it with a client policy so new clients cannot turn it back on.
  • Review the built-in clients in the next section.

Sign-in methods by grant​

Which methods a user can sign in with depends on the flow that runs, not just on what you configured in the browser flow:

Sign-in methodBrowser flowDirect grant flow
Username and passwordYesYes
Password + OTPYesOnly through the default conditional OTP step, for users who already have an OTP credential
Password + WebAuthn security keyYesNo
PasskeysYesNo
Magic link or email OTPYesNo
Social login and enterprise SSOYesNo
CAPTCHA (Turnstile or reCAPTCHA)YesNo
Required actions such as Update password or Verify emailYes, completed in the browserNo, the request fails instead

The direct grant flow​

The default direct grant flow is short:

StepRequirement
Username ValidationRequired
PasswordRequired
Direct Grant - Conditional OTPConditional
  Condition - user configuredRequired
  OTPRequired

Nothing you add to the browser flow appears here. A user who signs in with a passkey in the browser can still get a token with a password alone, if they have one, and there is no page to put a CAPTCHA on. RFC 9700, the OAuth 2.0 Security Best Current Practice, says the password grant must not be used.

The direct grant flow also answers differently depending on account state. A correct password for a user with a pending required action, such as Update password, returns Account is not fully set up instead of Invalid user credentials. That tells an attacker the password is right even though no token was issued. If you reset compromised accounts, do it by setting a new random password rather than only adding the Update password required action. See Credential Stuffing.

Built-in clients​

Every realm is created with the same six clients. Their defaults, as created in Keycloak 26.8.1:

ClientTypeStandard flowDirect access grantsFull scope allowedWhat it is forGo-live guidance
admin-cliPublicOffOnOnCommand-line tools such as kcadmTurn off Direct access grants. Move automation to service accounts.
security-admin-consolePublic, PKCEOnOffOnThe Admin Console login for the realmOn a Phase Two hosted cluster, keep it enabled and unchanged: the dashboard's Console link signs in through it. Restrict /admin paths with IP restrictions instead. Self-hosted, disable it if no realm administrators use it.
accountPublicOnOffOffHolds the account roles and is the audience for the Account REST APIKeep enabled. See Account Console.
account-consolePublic, PKCEOnOffOffThe end-user Account ConsoleDisable or deny it if your users should not self-manage through Keycloak. See Account Console.
realm-managementBearer-onlyNot applicableOffOffHolds the realm admin rolesLeave alone.
brokerBearer-onlyNot applicableOffOffHolds the identity broker rolesLeave alone.

For the reasoning behind each line, and what an automated scanner learns from each client, see Which Keycloak clients should be enabled when you go live?.

Your own clients deserve the same review. When you create one, check every switch on the Capability config step instead of accepting what is selected, and for each client confirm:

  • Only the grants it uses are on. Usually that is Standard flow for an application, or Service account roles for a backend.
  • Public clients require PKCE, with the S256 method.
  • Full scope allowed is off, with only the roles the client needs added under Client scopes.
  • Offline access is optional, not default, unless the client genuinely needs long-lived sessions. See Tokens and Sessions.