Skip to content

starschema/aifd-react-keycloak

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@hcl/aifd-react-keycloak

A React library for Keycloak authentication in React applications.

Overview

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.

Features

  • AuthProvider React context provider with full Keycloak lifecycle management
  • useAuth() hook exposing auth state and actions to any child component
  • apiFetch() authenticated fetch helper with proactive 30-second token refresh
  • Automatic token refresh on expiry via kc.onTokenExpired
  • Silent SSO check on load (check-sso strategy)
  • Cross-frame logout via postMessage with origin allowlist
  • Dual ESM + CJS build output

Peer Dependencies

Package Version
react ^18.0.0
keycloak-js ^26.0.0

These must be installed by the consuming application.

Installation

npm install @hcl/aifd-react-keycloak

Install peer dependencies (if not already present):

npm install react keycloak-js

Quick Start

import 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>
  )
}

API Reference

<AuthProvider> Props

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.

useAuth() return value

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

apiFetch(url, options?)

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.

AUTH_MESSAGE_TYPES

import { AUTH_MESSAGE_TYPES } from '@hcl/aifd-react-keycloak'
// AUTH_MESSAGE_TYPES.KEYCLOAK_LOGOUT === 'KEYCLOAK_LOGOUT'

Cross-Frame 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 origin must be in allowedHostOrigins.

Silent SSO Setup

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>

Building

npm run build

Build outputs:

  • dist/aifd-react-keycloak.js — ESM bundle
  • dist/aifd-react-keycloak.cjs — CommonJS bundle
  • Source maps included

Note: react, react/jsx-runtime, and keycloak-js are treated as externals and not bundled.

Project Structure

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

Contributing

  1. Clone the repository
  2. npm install
  3. npm run build to verify the library builds
  4. Make changes in src/
  5. Bump version in package.json following semver before opening a PR

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors