Skip to content

SPA Frontend

keycloak-js is the official browser-side adapter for Keycloak. It handles the Authorization Code + PKCE flow, token storage, silent refresh, and logout — all without you managing redirect URLs or token exchanges by hand.

Install it from npm:

Terminal window
npm install keycloak-js

The package ships its own TypeScript types, so no separate @types package is needed.

Pass your realm’s URL, your realm name, and your client ID. These three values are the minimum required to identify your application to Keycloak:

import Keycloak from 'keycloak-js';

const keycloak = new Keycloak({
  url: 'http://localhost:8080',
  realm: 'my-app',
  clientId: 'my-app-frontend',
});

keycloak
  .init({ onLoad: 'login-required', pkceMethod: 'S256' })
  .then((authenticated) => {
    if (authenticated) {
      console.log('Logged in as', keycloak.tokenParsed?.preferred_username);
      startApp();
    }
  });

function startApp() {
  // Your app bootstrap goes here.
  // keycloak.token is available from this point on.
}

Key points:

  • onLoad: 'login-required' redirects the user to Keycloak immediately if they are not yet authenticated. Use 'check-sso' if you want the page to be accessible without login and only show a login button.
  • pkceMethod: 'S256' enables PKCE (Proof Key for Code Exchange). This is mandatory for public clients (SPAs) because they cannot store a client secret securely. PKCE replaces the secret with a per-request code challenge.

After init resolves with authenticated: true, keycloak.token holds the raw JWT string. Attach it as a Bearer token in every API request:

async function fetchProfile() {
  await keycloak.updateToken(30); // refresh if expiring in < 30 seconds

  const response = await fetch('https://api.example.com/profile', {
    headers: {
      Authorization: `Bearer ${keycloak.token}`,
    },
  });

  return response.json();
}

updateToken(minValidity) checks whether the current token will expire within minValidity seconds and, if so, uses the refresh token to silently obtain a new one. Always call it before attaching keycloak.token to a request so you never send an expired JWT.

The decoded payload is available in keycloak.tokenParsed. Common claims you will use:

keycloak.tokenParsed?.preferred_username // username
keycloak.tokenParsed?.email // email address
keycloak.tokenParsed?.realm_access?.roles // array of realm roles
keycloak.tokenParsed?.resource_access?.['my-app-frontend']?.roles // client roles
What does the pkceMethod: "S256" option do when passed to keycloak.init()?
Why should you call keycloak.updateToken(30) before attaching the token to an API request?
Where does keycloak-js store the access token by default?
Which onLoad value redirects the user to Keycloak immediately if they are not authenticated?