Blog
Wild & Free Tools

How to Decode a Keycloak JWT: Roles, Scopes, and Token Claims

Last updated: April 2026 5 min read
Quick Answer

Table of Contents

  1. Keycloak Token Structure
  2. Realm Roles vs Client Roles
  3. Common Keycloak Token Issues
  4. Extract Username and Email
  5. Frequently Asked Questions

Keycloak JWTs have a distinctive structure — roles are nested inside realm_access and resource_access objects rather than a flat array. Paste your Keycloak token into the decoder above to inspect the full payload. Here is how to interpret what you see.

Decoded Keycloak Access Token — Full Structure

{
  "exp": 1700003600,
  "iat": 1700000000,
  "jti": "abc123-...",
  "iss": "https://keycloak.example.com/realms/myrealm",
  "aud": ["my-client", "account"],
  "sub": "user-uuid-here",
  "typ": "Bearer",
  "azp": "my-client",
  "session_state": "session-uuid",
  "scope": "openid email profile",
  "sid": "session-uuid",
  "email_verified": true,
  "name": "Jane Smith",
  "preferred_username": "janesmith",
  "given_name": "Jane",
  "family_name": "Smith",
  "email": "[email protected]",
  "realm_access": {
    "roles": ["default-roles-myrealm", "offline_access", "uma_authorization", "admin"]
  },
  "resource_access": {
    "my-client": {
      "roles": ["client-admin", "read-data"]
    },
    "account": {
      "roles": ["manage-account", "view-profile"]
    }
  }
}

realm_access vs resource_access: The Role Structure

Keycloak separates roles into two categories:

To check if a user has a realm role in JavaScript:

const payload = decodeJwt(token);
const realmRoles = payload.realm_access?.roles || [];
const hasAdminRole = realmRoles.includes('admin');

// Client-specific role
const clientRoles = payload.resource_access?.['my-client']?.roles || [];
const canReadData = clientRoles.includes('read-data');
Sell Custom Apparel — We Handle Printing & Free Shipping

Debugging Keycloak Token Problems by Decoding

Decoding a Keycloak token helps diagnose these common issues:

Reading Username and Email from Keycloak Token

Keycloak uses preferred_username (not just sub) for the human-readable username. The sub is a UUID that does not change even if the username changes.

const payload = decodeJwt(token);

const userId = payload.sub;                    // permanent UUID
const username = payload.preferred_username;   // display username (can change)
const email = payload.email;
const isEmailVerified = payload.email_verified;
const fullName = payload.name;
const issuer = payload.iss;
// Extract realm name from issuer
const realm = issuer.split('/realms/')[1];

For user identity, always use sub as the primary key in your database, not preferred_username — usernames can be changed by admins.

Decode Your Keycloak Token

Paste your Keycloak access token above — see realm_access, resource_access, and all user claims instantly.

Open Free JWT Decoder

Frequently Asked Questions

Why do I see "failed to parse jwt keycloak" errors?

This usually means your verification library received a token in the wrong format, the issuer URL has a trailing slash mismatch, or the JWKS endpoint is unreachable. Decode the raw token first to confirm it is structurally valid, then check your issuer URL configuration.

How do I get the Keycloak token in my application?

Use a Keycloak adapter or standard OpenID Connect library. After login, the token is in the keycloak.token property (JavaScript adapter), or returned in the token_endpoint response (standard OAuth2 flow).

Can I decode a Keycloak offline token?

Yes. Offline tokens are JWTs with offline_access scope. Decoding will show the token type and expiry — offline tokens typically have much longer or no expiry, but the refresh token they use is what persists server-side.

Andrew Walsh
Andrew Walsh Developer Tools & API Writer

Andrew worked as a developer advocate at two SaaS startups writing API documentation used by thousands of engineers.

More articles by Andrew →
Launch Your Own Clothing Brand — No Inventory, No Risk