Debug School

rakesh kumar
rakesh kumar

Posted on

How to Enable Refresh Token Exchange in Keycloak 26

What Is a Refresh Token?
Why Do We Need a Refresh Token?
What Is the Role of the Refresh Token?
Why Enable Refresh Token Exchange Specifically?
Relationship Between Refresh Token and User Session in Keycloak
Why “Service Accounts Enabled” Is a MUST in Keycloak Setup
Why Token Exchange Requires a Service Account
Service Account = Required for Refresh Token Exchange
How to Check If Service Accounts Are Enabled
What is the role of “Authorization” in Keycloak?
How to get client id and client secreat by command
Check if Service Account is Enabled
Fetch Service Account User
Trying to Fetch Realm Role Mappings (This fails)
Management Permissions — Feature Not Enabled

What Is a Refresh Token

In OAuth2 / OpenID Connect (OIDC), a refresh token is a special, long-lived credential issued to a user after login.

It is used for one purpose only:

❗ To obtain a new access token WITHOUT forcing the user to log in again.

Meaning:

Access token = short life (5 min, 15 min, 1 hour)

Refresh token = long life (days, months, until revoked)
Enter fullscreen mode Exit fullscreen mode

When the access token expires → the application calls Keycloak:

grant_type=refresh_token
Enter fullscreen mode Exit fullscreen mode

Keycloak validates the refresh token and issues:

a new access token

optionally a new refresh token

This allows the user to remain logged in seamlessly.

Why Do We Need a Refresh Token?

Without a refresh token, the user must log in again every time the access token expires.

Access tokens expire quickly for security reasons, so without a refresh token:

Mobile apps must redirect users repeatedly

Web apps would log out after expiration

API applications lose session continuity

User experience becomes frustrating

A refresh token solves this.

What Is the Role of the Refresh Token?

The refresh token has three main roles:

1️⃣ Maintain Long-Term User Sessions

Access tokens expire fast.
Refresh tokens maintain:

Session continuity

Long sessions without manual login

SSO across multiple apps
Enter fullscreen mode Exit fullscreen mode

A refresh token is basically the "life support" for your session.

2️⃣Avoid Logging in Again

When the access token expires, the client app simply uses:

grant_type=refresh_token

→ The user is NOT prompted for password
→ The app gets a fresh token pair instantly
→ The user remains logged in

This is essential for:

Mobile apps

React / Flutter apps

Multi-tab web apps

Cross-domain SSO
Enter fullscreen mode Exit fullscreen mode

3️⃣ Ensure Security

A refresh token ensures:

No need to store passwords in apps

No need to keep long-lived access tokens (dangerous!)

If stolen, it can be revoked independently

It is rotated frequently

Enter fullscreen mode Exit fullscreen mode

Keycloak also supports:

Refresh token rotation

Session revocation

Offline access tokens
Enter fullscreen mode Exit fullscreen mode

Why Enable Refresh Token Exchange Specifically?

In a normal login flow, refresh tokens are automatically issued.

But in SSO, multi-domain apps, and token exchange, this is different.

Keycloak does not allow generating refresh tokens through token exchange by default.

Meaning:

✔ Exchanging an access token → allowed
❌ Exchanging for refresh token → blocked

So if your architecture has:

Domain A logs in

Domain B wants seamless login

Domain C is mobile app

Backend microservices need long sessions

Drivers app, customers app, admin portal
Enter fullscreen mode Exit fullscreen mode

Then Domain B or Domain C must exchange the user’s token from Domain A.

Without refresh-token exchange:

Domain B cannot maintain session

User gets logged out after access token expires

SSO breaks after 5–15 minutes

Multi-domain apps cannot share session

Mobile apps cannot maintain login
Enter fullscreen mode Exit fullscreen mode

This is why you must manually enable:

Service accounts

Realm roles

FGAP permissions

Token-exchange feature
Enter fullscreen mode Exit fullscreen mode

Otherwise Keycloak does NOT permit issuing refresh tokens via token exchange.

Relationship Between Refresh Token and User Session in Keycloak

Keycloak tracks:

Refresh tokens

Session state

Session expiration
Enter fullscreen mode Exit fullscreen mode

When a refresh token is issued, Keycloak creates/updates a session record:

session_state = <UUID>
Enter fullscreen mode Exit fullscreen mode

This session state:

Appears under Admin → Users → Sessions

Is shared across multiple applications

Updates every time refresh token is used

Can be revoked instantly

So:

🔹 Issuing refresh tokens = creating session
🔹 Using refresh tokens = extending session
🔹 Revoking refresh token = ending session
Enter fullscreen mode Exit fullscreen mode

This is why refresh-token exchange is directly tied to user session creation and maintenance.

Why “Service Accounts Enabled” Is a MUST in Keycloak Setup

To correctly use Token Exchange, especially when you want Keycloak to issue refresh tokens, your Keycloak client MUST have:

✔ Client Authentication = ON
✔ Service Accounts Enabled = ON
Enter fullscreen mode Exit fullscreen mode

What Is a Service Account in Keycloak?

A Service Account is a special, internal Keycloak user automatically created for a client when you enable:

Service accounts roles = ON
Enter fullscreen mode Exit fullscreen mode

For example:

service-account-motoshare
Enter fullscreen mode Exit fullscreen mode

This “user” is NOT visible to normal login pages and does not require a password.

It exists only to perform secure backend operations, such as:

Token Exchange

Acting on behalf of the client

Requesting special types of tokens

Managing users (if assigned roles)

Obtaining refresh tokens through token exchange
Enter fullscreen mode Exit fullscreen mode

Why Token Exchange Requires a Service Account

When you perform Token Exchange:

grant_type = urn:ietf:params:oauth:grant-type:token-exchange
Enter fullscreen mode Exit fullscreen mode

Keycloak evaluates:

"Does this CLIENT have the permission to exchange tokens?"

Not the user.

The permission check is done on the client’s service account.

This means Keycloak asks internally:

Does service-account-motoshare have:

- token-exchange role?
- impersonation role?
- manage-users?
- view-users?
- query-users?
Enter fullscreen mode Exit fullscreen mode

If the service account user does NOT exist (because you did not enable service accounts),
then Keycloak cannot evaluate the permissions.

→ Refresh token exchange always fails
→ You get:

"requested_token_type unsupported"

Service Account = Required for Refresh Token Exchange

Keycloak never gives refresh tokens during exchange unless the client has a service account with the correct realm roles.

These roles are added to the service account user:

service-account-motoshare

Roles required:

token-exchange

impersonation

manage-users

view-users

query-users
Enter fullscreen mode Exit fullscreen mode

Without the service account existing:

These roles cannot be assigned

Fine-Grained Admin Permissions cannot be applied

Token-exchange permission cannot be created

Refresh tokens cannot be issued

SSO session cannot be created

This is why enabling Service Accounts is absolutely required.

How to Check If Service Accounts Are Enabled

UI

Go to:
Clients → your client → Settings → Capability Config

Ensure:

[ ON ] Client Authentication
[ ✔ ] Service accounts roles
Enter fullscreen mode Exit fullscreen mode

Command

./kcadm.sh get clients/<CLIENT_ID> -r <realm> | grep serviceAccountsEnabled
Enter fullscreen mode Exit fullscreen mode

Should return:


"serviceAccountsEnabled" : true
Enter fullscreen mode Exit fullscreen mode

What is the role of “Authorization” in Keycloak

?

How to get client id and client secreat by command

opt/auth.motoshare.in/bin# ./kcadm.sh get clients -r motoshare --fields id,clientId
Enter fullscreen mode Exit fullscreen mode

Check if Service Account is Enabled

./kcadm.sh get clients/9a3c0749-251e-4336-8562-86c51bcaa4b4 -r motoshare | grep serviceAccountsEnabled
Enter fullscreen mode Exit fullscreen mode

Output:

"serviceAccountsEnabled" : true,
Enter fullscreen mode Exit fullscreen mode

Fetch Service Account User

./kcadm.sh get clients/9a3c0749-251e-4336-8562-86c51bcaa4b4/service-account-user -r motoshare
Enter fullscreen mode Exit fullscreen mode

output

{
  "id" : "4864aeaf-3b01-4873-a175-17cb16a1383f",
  "username" : "service-account-motoshare",
  "emailVerified" : false,
  "enabled" : true,
  "createdTimestamp" : 1764826039506,
  "totp" : false,
  "disableableCredentialTypes" : [ ],
  "requiredActions" : [ ],
  "notBefore" : 0
}
Enter fullscreen mode Exit fullscreen mode

Trying to Fetch Realm Role Mappings (This fails)

./kcadm.sh get clients/9a3c0749-251e-4336-8562-86c51bcaa4b4/service-account-user/role-mappings/realm -r motoshare
Enter fullscreen mode Exit fullscreen mode

Management Permissions — Feature Not Enabled

./kcadm.sh get clients/9a3c0749-251e-4336-8562-86c51bcaa4b4/management/permissions -r motoshare
Enter fullscreen mode Exit fullscreen mode

ouptput
For more on this error consult the server log. [Feature not enabled]

==============================================================

Step-by-Step Setup with Commands
1️⃣ Login using KCADM

./kcadm.sh config credentials \
  --server https://auth.motoshare.in \
  --realm master \
  --user admin \
  --password 'YOUR_PASSWORD'
Enter fullscreen mode Exit fullscreen mode

2️⃣ Enable Keycloak Features

Edit:

/opt/keycloak/conf/keycloak.conf
Enter fullscreen mode Exit fullscreen mode

Add:

features=token-exchange
Enter fullscreen mode Exit fullscreen mode

Restart:

systemctl restart keycloak
Enter fullscreen mode Exit fullscreen mode

3️⃣ Enable Service Account for Client

UI or KCADM:

./kcadm.sh update clients/<CLIENT_ID> -r motoshare -s serviceAccountsEnabled=true
Enter fullscreen mode Exit fullscreen mode

4️⃣ Assign Required Realm Roles

./kcadm.sh add-roles \
  --uusername service-account-motoshare \
  --cclientid realm-management \
  -r motoshare \
  --rolename token-exchange \
  --rolename impersonation \
  --rolename manage-users \
  --rolename view-users \
  --rolename query-users
Enter fullscreen mode Exit fullscreen mode

5️⃣ Enable Fine-Grained Admin Permissions (FGAP)

./kcadm.sh update clients/<CLIENT_ID>/management/permissions -r motoshare -s enabled=true
Enter fullscreen mode Exit fullscreen mode

6️⃣ Add Token Exchange Permission

./kcadm.sh create clients/<CLIENT_ID>/management/permissions/token-exchange \
  -r motoshare \
  -b '{"type":"client", "client":"motoshare"}'
Enter fullscreen mode Exit fullscreen mode

7️⃣ TEST Refresh Token Exchange

curl -X POST "https://auth.motoshare.in/realms/motoshare/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  -d "client_id=motoshare" \
  -d "client_secret=YOUR_SECRET" \
  -d "subject_token=<ACCESS_TOKEN>" \
  -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "requested_token_type=urn:ietf:params:oauth:token-type:refresh_token"
Enter fullscreen mode Exit fullscreen mode

Top comments (0)