Skip to content

Token Endpoint Hands-on

The Direct Access Grant — also called the password grant — lets you exchange a username and password directly for tokens at the token endpoint, bypassing the browser redirect flow entirely. Because the user’s credentials pass through the client before reaching Keycloak, this grant is suitable only for test scripts, CLI tools, and development environments — never a real user-facing app.

Use the Direct Access Grant for:

  • Testing your Keycloak configuration from a terminal
  • CLI tools and shell scripts that need a token to call an API
  • Development workflows where you want to quickly inspect a token

It should not be used in real user-facing applications. When you hand a user’s password directly to your app, the app sees the credentials — which defeats the purpose of delegating authentication to Keycloak. For browser and mobile apps, always use Authorization Code + PKCE.

To use this grant, the client must have Direct access grants enabled on the Capability config tab.

Replace the placeholders with your values before running.

curl -X POST https://${KC_URL}/realms/${REALM}/protocol/openid-connect/token \
  -d "grant_type=password" \
  -d "client_id=${CLIENT_ID}" \
  -d "username=${USERNAME}" \
  -d "password=${PASSWORD}"

For a confidential client, also add -d "client_secret=<your-secret>" to the command.

A successful request returns a JSON object like this:

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"session_state": "abc123",
"scope": "openid profile email"
}

Key fields:

  • access_token — a JWT you send to your API as Authorization: Bearer <token>.
  • expires_in — the access token lifetime in seconds (300 = 5 minutes by default).
  • refresh_token — use this to get a new access token without re-authenticating.
  • id_token — a JWT that proves who the user is (OIDC only).
  • token_type — always Bearer for Keycloak.

Send the access token to an API with a Bearer Authorization header:

Authorization: Bearer <access_token>

The access token is a Base64url-encoded JWT. You can decode the payload to inspect its claims.

echo ${ACCESS_TOKEN} | cut -d. -f2 | base64 -d | jq .

On some systems use base64 --decode instead of base64 -d. Set the ACCESS_TOKEN environment variable to your token value first, or replace it inline.

OptionBenefitCost
Test directly against the token endpoint with curlSee the raw request/response clearly, with no abstraction hiding the real protocolYou must manage the token lifecycle yourself (refresh, expiry, storage), and risk leaking credentials if you’re not careful
Use an OIDC client library (e.g. openid-client, a Keycloak adapter)The library automatically handles token refresh, validation, and security best practiceAdds a dependency and an abstraction to learn, and you don’t see the raw protocol the way curl shows it
  • Typing a password or client secret directly into a curl command — commands typed in a terminal get recorded in shell history (.bash_history, .zsh_history) in plaintext. Use an environment variable or an .env file excluded from git instead of typing credentials directly.
  • Pasting an access token into a log or sharing it in chat to debug something — an access token or refresh token is as valuable as a password; anyone holding it before it expires can call the API as that user. Never log a token in full — mask it or log only a fragment.
  • Using the Direct Access Grant (password grant) in a real production app — this grant is meant only for test scripts and CLI tools, because it requires sending the username/password directly to the client. Using it in a real app needlessly exposes the user’s credentials to the client.
  • Not clearing HISTCONTROL or forgetting to clear history after testing with curl — forgetting to clean up leaked credentials in shell history after debugging leaves the secret sitting on your machine indefinitely.

Real-world example

Postman or Insomnia in a dev workflow — dev teams often use these tools instead of raw curl to keep credentials in an encrypted vault, rather than typing them on a command line that risks ending up in shell history.

kubectl and other CLI tools that speak OIDC — use the same underlying principle as a curl call to the token endpoint, but wrap it in a library that handles token caching, refresh, and secure storage automatically, unlike a raw curl script that has to do every step by hand.

What is the Direct Access Grant (password grant) primarily used for?
What header do you use to send an access token to an API?
What does expires_in represent in the token response?
Why should the password grant be avoided in real apps?