Lit-based web components and small framework helpers (React, Vue, Svelte, Solid) built on oidc-spa, with shared auth state via @lit/context.
pnpm add keycloak-headless oidc-spa
# or
npm install keycloak-headless oidc-spaApps must initialize oidc-spa before the UI loads. Scaffolded projects include this automatically:
oidcEarlyInitinsrc/main.ts(loadssrc/main.lazy.tswhen the app should start)oidcSpa()Vite plugin invite.config.ts
Manual setup:
// src/main.ts
import { oidcEarlyInit } from "oidc-spa/core";
const { shouldLoadApp } = oidcEarlyInit({
BASE_URL: import.meta.env.BASE_URL,
});
if (shouldLoadApp) {
void import("./main.lazy");
}// vite.config.ts
import { oidcSpa } from "oidc-spa/vite-plugin";
export default defineConfig({
plugins: [oidcSpa(), /* ... */],
});Pass base-url={import.meta.env.BASE_URL} (or equivalent) to <kc-provider> / KeycloakProvider so redirect URIs match your app path.
Keycloak client Valid Redirect URIs must be exact origin paths with a trailing slash (for example http://localhost:5173/), not wildcards like http://localhost:5173/*.
Scaffold a new Vite SPA wired up with keycloak-headless, typed realm roles, and framework helpers:
npx keycloak-headless createThe CLI asks which framework you want (React, Vue, Solid, or Svelte), then creates a project from the bundled templates. It also automatically:
- Installs project dependencies
- Starts local Keycloak via
keycloak-runner(or reuses an existing instance on port 8080) - Creates the public SPA client in Keycloak
Requirements: Java 17+, free port 8080, and network on first run (Keycloak is downloaded). Default admin credentials are admin / admin.
Non-interactive usage:
npx keycloak-headless create \
--framework react \
--name my-keycloak-app \
--url http://localhost:8080/ \
--realm master \
--client-id example-spa \
--yesAfter scaffolding:
cd my-keycloak-app
pnpm devEach scaffolded project includes a starter keycloak-roles.json fixture so keycloakRolesPlugin can generate typed roles on the first dev or build run. After you add custom roles in Keycloak, run pnpm fetch-roles to sync typed role definitions.
If Keycloak bootstrap fails, the project is still created — run pnpm run-keycloak manually and retry.
When developing this repository:
pnpm create-appDemo app changes belong in example/{react,vue,solid,svelte}/. After editing an example, regenerate the scaffold templates before committing:
pnpm sync-templatespnpm build runs this automatically. CI fails if templates/ drifts from example/ (pnpm sync-templates:check).
| Import | Purpose |
|---|---|
keycloak-headless |
authContext, AuthState, and subscribeAuthContext only (no custom element registration). |
keycloak-headless/provider |
Registers all kc-* elements and exports them plus KcProvider, authContext, subscribeAuthContext. |
keycloak-headless/react |
KeycloakProvider, useAuth, AuthBridge, and context helpers (registers kc-provider when used). |
keycloak-headless/vue |
useKeycloakAuth, useKeycloakConfigRoles, subscribeAuthContext, and AuthState (no custom element registration). |
keycloak-headless/svelte |
useKeycloakAuth store factory, subscribeAuthContext, and AuthState. |
keycloak-headless/solid |
useKeycloakAuth, subscribeAuthContext, and AuthState. |
keycloak-headless/vite |
keycloakRolesPlugin — generates typed KEYCLOAK_CONFIG from a realm roles export JSON file. |
Always import keycloak-headless/provider (side-effect or explicit imports) in apps that use the web components, so custom elements are defined.
Mount <kc-provider> with url, realm, and client-id (or the clientId property in frameworks). Optional scope, on-load, and base-url are forwarded to createOidc(). Nested components consume authContext.
kc-provider attributes:
| Attribute | Purpose |
|---|---|
url |
Keycloak server URL |
realm |
Realm name |
client-id |
Public SPA client ID |
on-load |
check-sso (default) or login-required (maps to oidc-spa autoLogin) |
scope |
OAuth scopes (space-separated, e.g. profile email) |
base-url |
App base path (default /; use import.meta.env.BASE_URL) |
enable-logging |
Enable oidc-spa debug logs |
redirect-uri |
Post-login redirect URL override |
retry-attempts |
Max init retries (default 3) |
retry-delay |
Initial retry delay in ms (default 1000) |
auto-retry |
Retry failed init (default true) |
| Element | Role |
|---|---|
<kc-provider> |
Initializes oidc-spa and provides auth context. |
<kc-login-button> |
Renders when the user can log in; click calls oidc.login(). |
<kc-logout-button> |
Renders when authenticated; click calls oidc.logout(). |
<kc-account-link> |
When authenticated, opens the Keycloak account console via oidc-spa Keycloak utils. |
<kc-render-authenticated> |
Slot visible only when authenticated is true. |
<kc-render-guest> |
Slot visible when oidc is ready and the user is not authenticated. |
<kc-render-roles> |
Slot when the user has required realm or client roles (roles, role-kind, match, optional resource). |
<kc-error-display> |
Displays errors with user-friendly messages and retry button. |
<kc-loading> |
Shows loading spinner during authentication. |
Slot-based buttons: put a real <button type="button"> (or other focusable control) inside each interactive component so keyboard and screen-reader behavior stay correct.
import "keycloak-headless/provider";
import { KeycloakProvider, useAuth } from "keycloak-headless/react";
export function App() {
return (
<KeycloakProvider
url="https://keycloak.example/auth/"
realm="myrealm"
clientId="my-spa"
>
<AuthPanel />
</KeycloakProvider>
);
}
function AuthPanel() {
const { oidc, authenticated, error } = useAuth();
// …
}KeycloakProvider renders <kc-provider> and AuthBridge, which bridges Lit context into AuthReactContext for useAuth().
Import keycloak-headless/vue after keycloak-headless/provider so custom elements are defined. Use a template ref on a normal element inside <kc-provider>:
<script setup lang="ts">
import "keycloak-headless/provider";
import { computed, ref } from "vue";
import { useKeycloakAuth, useKeycloakConfigRoles } from "keycloak-headless/vue";
import { KEYCLOAK_CONFIG } from "./keycloak-config.generated.js";
const hostRef = ref<HTMLElement | null>(null);
const auth = useKeycloakAuth(hostRef);
const roleChecks = useKeycloakConfigRoles(auth, KEYCLOAK_CONFIG);
const showAdmin = computed(() => roleChecks.value.hasRealmRole("admin"));
</script>
<template>
<kc-provider url="…" realm="…" client-id="…">
<div ref="hostRef">
<p v-if="showAdmin">Has admin role</p>
<kc-render-roles :roles="realmRolesAttr('admin')" role-kind="realm" match="any">
…
</kc-render-roles>
</div>
</kc-provider>
</template>useKeycloakAuth mirrors the React AuthBridge pattern: it subscribes to Lit context and tears down when the host ref or component scope changes. Pair it with keycloakRolesPlugin (see Typed realm roles) and useKeycloakConfigRoles for the same compile-time role names as the React example.
<script lang="ts">
import "keycloak-headless/provider";
import { useKeycloakAuth } from "keycloak-headless/svelte";
let host: HTMLDivElement | undefined;
const auth = useKeycloakAuth(() => host ?? null);
</script>
<kc-provider url="…" realm="…" client-id="…">
<div bind:this={host}>{$auth.oidc ? "Ready" : "…"}</div>
</kc-provider>The store updates when bind:this attaches the host node.
import "keycloak-headless/provider";
import { useKeycloakAuth } from "keycloak-headless/solid";
export function Panel() {
let host: HTMLDivElement | undefined;
const auth = useKeycloakAuth(() => host ?? null);
return (
<div ref={(el) => (host = el)}>{auth().oidc ? "Ready" : "…"}</div>
);
}When using the optional headless-role-created Keycloak provider, realm and client roles are exported to {realm}-roles.json. The Vite plugin watches that file and regenerates a typed config on change.
// vite.config.ts
import { keycloakRolesPlugin } from "keycloak-headless/vite";
export default defineConfig({
plugins: [
keycloakRolesPlugin({
input: process.env.KEYCLOAK_ROLES_EXPORT ?? "/tmp/keycloak-role-exports/master-roles.json",
output: "src/keycloak-config.generated.ts",
}),
],
});Generated output (do not edit):
export const KEYCLOAK_CONFIG = {
realm: "master",
exportedAt: 1710000000000,
roles: ["admin", "editor"] as const,
clientRoles: { "my-spa": ["read"] as const },
} as const;
export type RealmRole = (typeof KEYCLOAK_CONFIG.roles)[number];React:
import { useKeycloakConfigRoles } from "keycloak-headless/react";
import { KEYCLOAK_CONFIG } from "./keycloak-config.generated.js";
const { hasRealmRole } = useKeycloakConfigRoles(KEYCLOAK_CONFIG);
hasRealmRole("admin"); // ok
hasRealmRole("typo"); // TypeScript errorThe export defines which roles exist in the realm; hasRealmRole checks the current user's ID token claims (realm_access.roles). Regenerate when roles change in Keycloak (the plugin does this automatically in dev when the JSON file changes).
See example/react/vite.config.ts and example/vue/vite.config.ts for working setups using the committed fixture at scripts/fixtures/master-roles.json.
Example pnpm build scripts run tsc --noEmit before Vite, so invalid role names fail the build. Run pnpm build at the repo root first so workspace keycloak-headless declaration files are up to date.
subscribeAuthContext from keycloak-headless or keycloak-headless/provider dispatches a Lit context request from any Element under <kc-provider>. It returns a disposer so you can unsubscribe on teardown. The React AuthBridge, useKeycloakAuth helpers above, and framework examples all build on this.
AuthState includes:
oidc— oidc-spa client (Oidcfromoidc-spa/core) after initialization.authenticated— boolean when known (oidc.isUserLoggedIn).error— set if initialization fails. Cleared on successful auth sync.loading— boolean indicating if authentication is in progress.loadingMessage— optional message to display during loading.
With oidc-spa, auth state is stable after init; login, logout, and session expiry typically cause redirects or full page reloads rather than in-place state toggles.
Narrow errors in UI, for example:
function message(err: unknown): string {
if (err instanceof Error) return err.message;
return String(err);
}The library provides comprehensive error handling with typed errors, automatic retry, user-friendly messages, and recovery strategies.
All Keycloak errors extend KeycloakError with structured error codes:
import { KeycloakError, ErrorCodes } from "keycloak-headless";
// Error types:
// - KeycloakInitError - Initialization failures
// - KeycloakAuthError - Authentication failures
// - KeycloakTokenError - Token refresh failures
// - KeycloakConfigError - Configuration errors
// - KeycloakNetworkError - Network errors
if (error instanceof KeycloakError) {
console.log(error.code); // e.g., "KC_INIT_1001"
console.log(error.userMessage); // User-friendly message
console.log(error.suggestedAction); // Recovery suggestion
console.log(error.recoverable); // Can retry?
}<kc-provider> automatically retries failed initialization with exponential backoff:
<kc-provider
url="https://keycloak.example.com/"
realm="myrealm"
client-id="my-spa"
retry-attempts="3"
retry-delay="1000"
auto-retry="true">
</kc-provider>Retry Attributes:
retry-attempts- Maximum retry attempts (default: 3)retry-delay- Initial delay in ms (default: 1000)auto-retry- Enable automatic retry (default: true)
Listen for error events to handle failures:
const provider = document.querySelector('kc-provider');
// Error occurred
provider.addEventListener('kc-error', (e) => {
const { error, canRetry, timestamp } = e.detail;
console.error(error.userMessage);
if (canRetry) showRetryButton();
});
// Retry attempt
provider.addEventListener('kc-retry', (e) => {
const { attempt, maxAttempts, delay } = e.detail;
console.log(`Retry ${attempt}/${maxAttempts} in ${delay}ms`);
});
// State change
provider.addEventListener('kc-state-change', (e) => {
const { previous, current } = e.detail;
console.log('Auth state changed', current);
});Or use callbacks:
provider.onError = (error) => {
console.error(error.userMessage);
};
provider.onRetry = ({ attempt, maxAttempts }) => {
console.log(`Retrying ${attempt}/${maxAttempts}`);
};<kc-provider url="..." realm="..." client-id="...">
<kc-loading message="Authenticating..."></kc-loading>
<kc-error-display show-retry></kc-error-display>
<kc-render-authenticated>
<!-- Your app -->
</kc-render-authenticated>
</kc-provider><kc-error-display> automatically shows errors from the auth context with:
- User-friendly error messages
- Suggested recovery actions
- Retry button (if recoverable)
- Technical details toggle
<kc-loading> shows a spinner during initialization and retries.
Display authentication errors from the auth context:
import { ErrorDisplay } from "keycloak-headless/react";
function App() {
const { error } = useAuth();
const providerRef = useRef<HTMLElement>();
return (
<>
{error && (
<ErrorDisplay
error={error}
onRetry={() => providerRef.current?.retry()}
/>
)}
<YourApp />
</>
);
}ErrorBoundary (Optional) - Use if you want to catch Keycloak errors thrown during render:
The provided ErrorBoundary is optional and designed for specific use cases:
- Wrap KeycloakProvider to catch initialization errors:
import { ErrorBoundary } from "keycloak-headless/react";
<ErrorBoundary onError={(error) => logToService(error)}>
<KeycloakProvider url="..." realm="..." clientId="...">
<App />
</KeycloakProvider>
</ErrorBoundary>- Use with your existing error boundary - The library exports
KeycloakErrortypes that your error boundary can handle:
// Your existing error boundary
class AppErrorBoundary extends React.Component {
componentDidCatch(error: Error) {
if (error instanceof KeycloakError) {
// Handle Keycloak-specific errors
console.log(error.userMessage, error.suggestedAction);
}
// Handle other errors...
}
}- Custom fallback UI:
<ErrorBoundary
fallback={(error, reset) => (
<div>
<h1>{error.userMessage}</h1>
<p>{error.suggestedAction}</p>
<button onClick={reset}>Try Again</button>
</div>
)}
>
<KeycloakProvider>...</KeycloakProvider>
</ErrorBoundary>Note: Most apps should handle auth errors via the error property in useAuth() rather than error boundaries. The ErrorBoundary is provided for convenience but is entirely optional.
Programmatically retry initialization:
const provider = document.querySelector('kc-provider');
await provider.retry();React:
const providerRef = useRef<HTMLElement>();
// Later:
await providerRef.current?.retry();Configure custom error logging:
import {
ConsoleErrorLogger,
MemoryErrorLogger,
RemoteErrorLogger,
CompositeErrorLogger
} from "keycloak-headless";
// Console logger (default)
const consoleLogger = new ConsoleErrorLogger(100); // max 100 entries
// Memory logger (for testing)
const memoryLogger = new MemoryErrorLogger(50);
// Remote logger (sends to endpoint)
const remoteLogger = new RemoteErrorLogger('https://api.example.com/errors', {
batchSize: 10,
flushInterval: 30000, // 30 seconds
});
// Composite logger (multiple destinations)
const logger = new CompositeErrorLogger([
consoleLogger,
remoteLogger,
]);
// Set on provider
provider.errorLogger = logger;
// Get logged errors
const errors = logger.getErrors();Network Errors - Automatically retried with exponential backoff.
Configuration Errors - Not retried (require code changes).
Token Refresh Failures - User should re-authenticate:
provider.addEventListener('auth-refresh-error', () => {
// Show login prompt
provider.authData.oidc?.login();
});Session Expired - Redirect to login:
provider.addEventListener('token-expired', () => {
const oidc = provider.authData.oidc;
if (oidc != null && oidc.isUserLoggedIn === false) {
void oidc.login();
}
});Main provider component that initializes Keycloak and provides auth context.
Required Attributes:
url- Keycloak server URLrealm- Realm nameclient-id- Client ID
Optional Attributes:
on-load-"check-sso"(default) or"login-required"scope- OAuth scopes (space-separated)pkce-method-"S256"(recommended) or"false"- See full list above
Example:
<kc-provider url="https://keycloak.example.com/" realm="myrealm" client-id="my-spa">
<!-- Your app content -->
</kc-provider>Renders slot content when user can log in. Click triggers login.
Example:
<kc-login-button>
<button>Login</button>
</kc-login-button>Renders slot content when authenticated. Click triggers logout.
Example:
<kc-logout-button>
<button>Logout</button>
</kc-logout-button>Opens Keycloak Account Console when clicked.
Optional Attributes:
redirect-uri- URI to return to after leaving Account Console
Example:
<kc-account-link redirect-uri="https://myapp.com/profile">
<a href="#">My Account</a>
</kc-account-link>Conditionally renders content only when authenticated.
Example:
<kc-render-authenticated>
<p>Welcome back!</p>
</kc-render-authenticated>Conditionally renders content only when NOT authenticated.
Example:
<kc-render-guest>
<p>Please log in</p>
</kc-render-guest>Conditionally renders based on user roles.
Attributes:
roles- Comma-separated role names (e.g.,"admin,editor")match-"any"(default, OR logic) or"all"(AND logic)role-kind-"realm"(default) or"client"resource- Client ID (required for client roles)
Examples:
<!-- Realm role - single -->
<kc-render-roles roles="admin">
<button>Admin Panel</button>
</kc-render-roles>
<!-- Multiple roles - any match -->
<kc-render-roles roles="admin,moderator" match="any">
<div>Admin or Moderator content</div>
</kc-render-roles>
<!-- Client roles -->
<kc-render-roles roles="manage-users" role-kind="client" resource="my-client">
<div>User Management</div>
</kc-render-roles>React wrapper for <kc-provider>.
Props: Same as <kc-provider> attributes (camelCase)
Example:
<KeycloakProvider url="..." realm="..." clientId="...">
<App />
</KeycloakProvider>Hook to access authentication state.
Returns: AuthState object with:
oidc- oidc-spa clientauthenticated- boolean or undefinederror- Error if any
Example:
const { oidc, authenticated, error } = useAuth();Type-safe realm role checking.
Parameters:
roles- Array of role names (useas const)
Returns: Object with hasRealmRole(role) function
Example:
const { hasRealmRole } = useRealmRoles(['admin', 'editor'] as const);
if (hasRealmRole('admin')) { /* ... */ }Type-safe role checking with generated config.
Parameters:
config- GeneratedKEYCLOAK_CONFIGobject
Returns: Object with hasRealmRole(role) and hasClientRole(client, role) functions
Example:
const { hasRealmRole, hasClientRole } = useKeycloakConfigRoles(KEYCLOAK_CONFIG);Composable for accessing auth state.
Parameters:
hostRef- Ref to an element inside<kc-provider>
Returns: Ref to AuthState
Example:
<script setup>
const hostRef = ref(null);
const auth = useKeycloakAuth(hostRef);
</script>
<template>
<div ref="hostRef">{{ auth.authenticated }}</div>
</template>Composable for type-safe role checking.
Parameters:
auth- Auth state fromuseKeycloakAuthconfig- GeneratedKEYCLOAK_CONFIG
Returns: Computed ref with role checker functions
Store factory for auth state.
Parameters:
getHost- Function returning host element
Returns: Readable store with AuthState
Example:
<script>
let host;
const auth = useKeycloakAuth(() => host ?? null);
</script>
<div bind:this={host}>{$auth.authenticated}</div>Signal-based auth state accessor.
Parameters:
getHost- Function returning host element
Returns: Accessor function returning AuthState
Example:
let host;
const auth = useKeycloakAuth(() => host ?? null);
return <div ref={host}>{auth().authenticated}</div>;Generates typed config from Keycloak roles export.
Options:
input- Path to{realm}-roles.jsonfileoutput- Path for generated TypeScript file
Example:
// vite.config.ts
import { keycloakRolesPlugin } from 'keycloak-headless/vite';
export default defineConfig({
plugins: [
keycloakRolesPlugin({
input: '/tmp/keycloak-role-exports/master-roles.json',
output: 'src/keycloak-config.generated.ts',
}),
],
});Symptoms: Console error on page load, authentication doesn't start
Causes:
- Incorrect Keycloak server URL
- Realm or client-id doesn't exist
- CORS not configured in Keycloak
- Network connectivity issues
Solutions:
- Verify URL format includes
/auth/suffix for older Keycloak versions (pre-17), or just base URL for Keycloak 17+ - Check realm and client exist in Keycloak Admin Console
- Add your app's origin to "Web Origins" in client settings (e.g.,
http://localhost:5173for dev) - Check browser DevTools Network tab for failed requests
- Ensure client is set to "Public" access type for SPAs
Symptoms: Page keeps redirecting to Keycloak and back
Causes:
redirect-urimismatch- Invalid client configuration
- Session cookie issues
Solutions:
- Ensure "Valid Redirect URIs" in Keycloak matches your app URL exactly (including protocol and port)
- Use
on-load="check-sso"instead of"login-required"for SPAs to avoid forced redirects - Clear browser cookies and try again
- Check for
redirectUritypos in configuration - Verify "Valid Post Logout Redirect URIs" is also configured
Symptoms: <kc-render-roles> doesn't show content despite having roles
Causes:
- Role not assigned to user
- Wrong
role-kind(realm vs client) - Incorrect
resourcefor client roles - Token not refreshed after role changes
Solutions:
- Verify role assignment in Keycloak Admin Console under Users → Role Mapping
- Use
role-kind="realm"for realm roles,"client"for client roles - For client roles, set
resourceto the exact client ID where the role is defined - Log out and log back in to get fresh token with new roles
- Check token contents in browser DevTools → Application → Cookies or use jwt.io
Symptoms: Type errors when using KEYCLOAK_CONFIG
Causes:
- Generated file not created
- Vite plugin not running
- Stale generated file
Solutions:
- Ensure
keycloakRolesPluginis invite.config.tsplugins array - Check that input JSON file exists and is valid
- Delete generated file and restart dev server
- Run
pnpm buildto regenerate types - Verify the JSON file path is correct (absolute or relative to project root)
Symptoms: User logged out unexpectedly, "Token refresh failed" errors
Causes:
- Refresh token expired
- Session timeout in Keycloak
- Network issues during refresh
Solutions:
- Increase "SSO Session Idle" timeout in Keycloak realm settings (default is 30 minutes)
- Increase "SSO Session Max" for longer sessions
- Implement token refresh error handling in your app
- Use
onAuthRefreshErrorcallback to prompt re-login - Consider implementing automatic token refresh before expiry
Symptoms: Web components don't appear or show as unknown elements
Causes:
- Forgot to import
keycloak-headless/provider - Import order issues
- Custom elements not supported
Solutions:
- Always import
keycloak-headless/providerbefore using components - Import provider at the top of your entry file (e.g.,
main.tsx) - Check browser console for "Failed to execute 'define' on 'CustomElementRegistry'" errors
- Verify browser supports Custom Elements (Chrome 90+, Firefox 88+, Safari 14+)
Symptoms: Network requests blocked by CORS policy
Causes:
- Web Origins not configured in Keycloak
- Incorrect origin format
Solutions:
- In Keycloak Admin Console, go to Clients → Your Client → Settings
- Add your app's origin to "Web Origins" (e.g.,
http://localhost:5173,https://myapp.com) - Use
*for development only (not recommended for production) - Ensure protocol (http/https) matches exactly
- Check that Keycloak server is accessible from your network
- Minimum: Chrome 90+, Firefox 88+, Safari 14+, Edge 90+
- Required: ES2021 support, Custom Elements v1, Shadow DOM
- Polyfills: Not provided - use modern browsers or add your own polyfills
-
Lazy Load Components: Import components only when needed
const AdminPanel = lazy(() => import('./AdminPanel'));
-
Minimize Token Checks: Cache role check results in components
const isAdmin = useMemo(() => hasRealmRole('admin'), [hasRealmRole]);
-
Use
check-sso: Avoid forced login on every page load<kc-provider on-load="check-sso" ...>
-
Enable Token Refresh: Set appropriate session timeouts in Keycloak
- SSO Session Idle: 30 minutes (default)
- SSO Session Max: 10 hours (adjust based on security requirements)
-
Optimize Bundle Size: Import only what you need
// Good: Import specific hooks import { useAuth } from 'keycloak-headless/react'; // Avoid: Importing everything import * as Keycloak from 'keycloak-headless/react';
- GitHub Issues: https://github.com/keycloak/keycloak-headless/issues
- Discussions: https://github.com/keycloak/keycloak-headless/discussions
- Keycloak Docs: https://www.keycloak.org/docs/latest/
- Keycloak Discourse: https://keycloak.discourse.group/
When reporting issues, please include:
- Keycloak version
- Browser and version
- Framework and version (React, Vue, etc.)
- Minimal reproduction code
- Console errors and network logs
Version 0.1.0 replaces keycloak-js with native oidc-spa.
Breaking changes:
AuthState.keycloak→AuthState.oidc(Oidcfromoidc-spa/core)- Add
oidc-spa,oidcEarlyInitentry split, andoidcSpa()Vite plugin (see oidc-spa setup) - Pass
base-url/baseUrlmatchingimport.meta.env.BASE_URLto the provider - Keycloak client redirect URIs: use exact paths with trailing slash (e.g.
http://localhost:5173/), not/*wildcards - Removed
kc-providerattributes: iframe SSO, implicit/hybrid flow, PKCE disable, token injection, Cordova adapter, etc. - Role helpers read ID token claims instead of
keycloak.hasRealmRole()
Example:
- const { keycloak, authenticated } = useAuth();
+ const { oidc, authenticated } = useAuth();
- const token = keycloak?.token;
+ const token = oidc?.isUserLoggedIn ? (await oidc.getTokens()).accessToken : undefined;See oidc-spa docs for DPoP, auto logout, and security features.
Requirements: Node.js 18+, pnpm.
pnpm install
pnpm build # library build to dist/
pnpm test # Vitest
pnpm lint # ESLint
pnpm typecheck # tsc --noEmit
pnpm example:react
pnpm example:vue
pnpm example:solid
pnpm example:svelte
pnpm create-appThe optional headless-role-created listener exports realm and client roles to {realm}-roles.json when roles are created via the Admin API. For local development, install the npm package keycloak-headless-role-created (pre-built JAR) and pass it to keycloak-runner with -p, as in example/react/package.json.
This will ensure that the roles are typechecked when you are creating your project.
Apache-2.0