A React library for Keycloak authentication in React applications.
A React library that wraps keycloak-js in a Context + Hook pattern,
providing a consistent and reusable authentication layer. It supports
both standalone apps and apps embedded as iframes inside a host page.
AuthProviderReact context provider with full Keycloak lifecycle managementuseAuth()hook exposing auth state and actions to any child componentapiFetch()authenticated fetch helper with proactive 30-second token refresh- Automatic token refresh on expiry via
kc.onTokenExpired - Silent SSO check on load (
check-ssostrategy) - Cross-frame logout via
postMessagewith origin allowlist - Dual ESM + CJS build output
| Package | Version |
|---|---|
react |
^18.0.0 |
keycloak-js |
^26.0.0 |
These must be installed by the consuming application.
npm install @hcl/aifd-react-keycloakInstall peer dependencies (if not already present):
npm install react keycloak-jsimport React from 'react'
import { AuthProvider, useAuth } from '@hcl/aifd-react-keycloak'
function ProtectedPage() {
const { user, authenticated, loading, logout, apiFetch } = useAuth()
if (loading) return <div>Loading...</div>
if (!authenticated) return <div>Not authenticated</div>
const handleLoadData = async () => {
const response = await apiFetch('/api/data', { method: 'GET' })
const data = await response.json()
console.log(data)
}
return (
<div>
<div>Welcome {user?.preferred_username}</div>
<button onClick={handleLoadData}>Load data</button>
<button onClick={logout}>Logout</button>
</div>
)
}
export default function App() {
return (
<AuthProvider
keycloakUrl="https://auth.example.com"
realm="my-realm"
clientId="my-client-id"
fallback={<div>Loading...</div>}
allowedHostOrigins={["https://host.example.com"]}
>
<ProtectedPage />
</AuthProvider>
)
}| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
keycloakUrl |
string |
Yes | — | Base URL of the Keycloak server (e.g. https://auth.example.com) |
realm |
string |
Yes | — | Keycloak realm name |
clientId |
string |
Yes | — | Keycloak client ID |
children |
ReactNode |
Yes | — | Child components rendered after authentication |
allowedHostOrigins |
string[] |
No | [] |
Whitelisted origins for cross-frame postMessage logout events |
initOptions |
object |
No | {} |
Additional/override options passed to keycloak.init(). Merged with the library defaults (onLoad: 'check-sso', silentCheckSsoRedirectUri) |
fallback |
ReactNode |
No | null |
Element rendered while Keycloak is initialising |
Note: AuthProvider throws an Error at render time if any of keycloakUrl, realm, or
clientId is missing.
| Field | Type | Description |
|---|---|---|
authenticated |
boolean |
true when the user has a valid Keycloak session |
loading |
boolean |
true while Keycloak is initialising |
user |
object | null |
Parsed JWT token payload (kc.tokenParsed); null when unauthenticated |
token |
string | null |
Raw JWT access token string; null when unauthenticated |
authError |
Error | null |
Non-null if Keycloak initialisation failed |
keycloak |
Keycloak | null |
The raw keycloak-js instance; null before init completes |
login |
() => void |
Triggers a Keycloak login redirect |
logout |
() => void |
Triggers a Keycloak logout redirect |
apiFetch |
(url, options?) => Promise<Response> |
Authenticated fetch — auto-refreshes token before every call |
You can import apiFetch directly, but its standalone signature expects a Keycloak
instance as the first argument. When consumed from useAuth().apiFetch, the instance is
already bound.
// via useAuth (recommended)
const { apiFetch } = useAuth()
const response = await apiFetch('/api/data', { method: 'GET' })
// standalone (advanced)
import { apiFetch } from '@hcl/aifd-react-keycloak'
const response = await apiFetch(keycloakInstance, '/api/data', { method: 'GET' })apiFetch will refresh the token if it expires within 30 seconds and will redirect to
login if the user is unauthenticated.
import { AUTH_MESSAGE_TYPES } from '@hcl/aifd-react-keycloak'
// AUTH_MESSAGE_TYPES.KEYCLOAK_LOGOUT === 'KEYCLOAK_LOGOUT'For child apps running in an iframe, the host shell can trigger a logout via
postMessage. Configure the allowed host origins on AuthProvider:
<AuthProvider
keycloakUrl="https://auth.example.com"
realm="my-realm"
clientId="my-client-id"
allowedHostOrigins={["https://host.example.com"]}
>
<App />
</AuthProvider>Host example:
iframeEl.contentWindow.postMessage(
{ type: 'KEYCLOAK_LOGOUT' },
'https://child-app.example.com'
)Security notes:
- Messages are only accepted from
window.parent(direct parent frame). - The message sender's
originmust be inallowedHostOrigins.
The library defaults to onLoad: 'check-sso', which requires a
/silent-check-sso.html file to be publicly accessible at the root of the consuming
application. Create this file with the following contents:
<html>
<body>
<script>
parent.postMessage(location.href, location.origin)
</script>
</body>
</html>If you prefer to skip silent SSO, pass custom initOptions, for example:
<AuthProvider
keycloakUrl="https://auth.example.com"
realm="my-realm"
clientId="my-client-id"
initOptions={{ onLoad: 'login-required' }}
>
<App />
</AuthProvider>npm run buildBuild outputs:
dist/aifd-react-keycloak.js— ESM bundledist/aifd-react-keycloak.cjs— CommonJS bundle- Source maps included
Note: react, react/jsx-runtime, and keycloak-js are treated as externals and not
bundled.
src/
├── index.js # Public barrel — re-exports all public symbols
├── keycloak.jsx # AuthProvider component and useAuth hook
├── apiFetch.js # Authenticated fetch helper
└── constants.js # AUTH_MESSAGE_TYPES enum
- Clone the repository
npm installnpm run buildto verify the library builds- Make changes in
src/ - Bump
versioninpackage.jsonfollowing semver before opening a PR