Authentication
@granit/authentication defines framework-agnostic Keycloak/OIDC types — user claims,
login/logout options, lifecycle events, and a base auth context interface. @granit/react-authentication
provides the useKeycloakInit hook that handles PKCE initialization, silent SSO, automatic
token refresh, and back-channel logout. It also exports createAuthContext and createMockProvider
factories so each consuming app can define its own typed auth context.
@granit/authentication-api-keys and @granit/react-authentication-api-keys add API key
management — types and React Query hooks for creating, revoking, rotating, and scoping keys.
Peer dependencies: keycloak-js, react ^19, axios, @tanstack/react-query ^5
Package structure
Section titled “Package structure”Directory@granit/authentication/ Keycloak/OIDC types (framework-agnostic)
- @granit/react-authentication Keycloak init hook, auth context factory, mock provider
Directory@granit/authentication-api-keys/ API key management types
- @granit/react-authentication-api-keys React Query hooks for API key CRUD
| Package | Role | Depends on |
|---|---|---|
@granit/authentication | Keycloak types, login/logout options, lifecycle events | keycloak-js |
@granit/react-authentication | useKeycloakInit, createAuthContext, createMockProvider | @granit/authentication, @granit/api-client, react, keycloak-js |
@granit/authentication-api-keys | API key DTOs (ApiKeyResponse, ApiKeyCreateRequest, etc.) | — |
@granit/react-authentication-api-keys | useApiKeys, useCreateApiKey, useRevokeApiKey, useRotateApiKey | @granit/authentication-api-keys, @tanstack/react-query, axios |
Dependency graph
Section titled “Dependency graph”graph TD
authn["@granit/authentication"]
reactAuthn["@granit/react-authentication"]
apiKeys["@granit/authentication-api-keys"]
reactApiKeys["@granit/react-authentication-api-keys"]
apiClient["@granit/api-client"]
keycloak["keycloak-js"]
reactAuthn --> authn
reactAuthn --> apiClient
reactAuthn --> keycloak
authn --> keycloak
reactApiKeys --> apiKeys
style keycloak fill:#e8f5e9,stroke:#43a047,color:#1b5e20
import type { BaseAuthContextType, KeycloakUserInfo } from '@granit/authentication';import { createAuthContext } from '@granit/react-authentication';
interface AuthContextType extends BaseAuthContextType { register: (options?: { redirectUri?: string }) => void;}
export const { AuthContext, useAuth } = createAuthContext<AuthContextType>();import { useKeycloakInit } from '@granit/react-authentication';import { AuthContext } from './auth-context';
export function AuthProvider({ children }: { children: React.ReactNode }) { const auth = useKeycloakInit({ url: import.meta.env.VITE_KEYCLOAK_URL, realm: import.meta.env.VITE_KEYCLOAK_REALM, clientId: import.meta.env.VITE_KEYCLOAK_CLIENT_ID, });
return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>;}import type { BaseAuthContextType, KeycloakUserInfo, LoginOptions, LogoutOptions,} from '@granit/authentication';
// Use the types to build your own auth layer (Angular, Vue, etc.)TypeScript SDK
Section titled “TypeScript SDK”KeycloakUserInfo
Section titled “KeycloakUserInfo”Standard OIDC user claims from the Keycloak /userinfo endpoint.
interface KeycloakUserInfo { sub: string; email?: string; email_verified?: boolean; name?: string; preferred_username?: string; given_name?: string; family_name?: string; picture?: string;}KeycloakEvent
Section titled “KeycloakEvent”Keycloak lifecycle event names.
type KeycloakEvent = | 'onReady' | 'onAuthSuccess' | 'onAuthError' | 'onAuthRefreshSuccess' | 'onAuthRefreshError' | 'onAuthLogout' | 'onTokenExpired';LoginOptions
Section titled “LoginOptions”interface LoginOptions { redirectUri?: string; idpHint?: string; // bypass Keycloak login page (e.g. "google") loginHint?: string; // pre-fill username locale?: string; // force login UI locale action?: string; // "register" or a required action name prompt?: 'login' | 'consent' | 'none'; scope?: string; // additional OAuth scopes maxAge?: number; // max seconds since last authentication}LogoutOptions
Section titled “LogoutOptions”interface LogoutOptions { redirectUri?: string;}BaseAuthContextType
Section titled “BaseAuthContextType”Shared auth context interface. Each consuming app extends it with application-specific fields.
interface BaseAuthContextType { keycloak: Keycloak | null; authenticated: boolean; loading: boolean; user: KeycloakUserInfo | null; login: () => void; logout: () => void;}KeycloakCoreConfig
Section titled “KeycloakCoreConfig”Configuration object for useKeycloakInit.
interface KeycloakCoreConfig { url: string; realm: string; clientId: string; silentCheckSso?: boolean; // default: true silentCheckSsoFallback?: boolean; // default: true (Safari workaround) useTokenClaims?: boolean; // default: false (use /userinfo endpoint) onTokenExpired?: () => void; onAuthRefreshError?: () => void; onAuthLogout?: () => void; onEvent?: (event: KeycloakEvent) => void;}API key types
Section titled “API key types”ApiKeyType
Section titled “ApiKeyType”type ApiKeyType = 'Secret' | 'Publishable' | 'Webhook' | 'Ephemeral';CacheBehavior
Section titled “CacheBehavior”type CacheBehavior = 'Normal' | 'NoCache';ApiKeyResponse
Section titled “ApiKeyResponse”interface ApiKeyResponse { readonly id: string; readonly name: string; readonly type: ApiKeyType; readonly environment: string; readonly prefix: string; readonly lastFourChars: string; readonly permissions: readonly string[]; readonly allowedCidrs: readonly string[]; readonly expiresAt: string | null; readonly lastUsedAt: string | null; readonly revokedAt: string | null; readonly cacheBehavior: CacheBehavior; readonly createdAt: string;}ApiKeyCreateRequest
Section titled “ApiKeyCreateRequest”interface ApiKeyCreateRequest { readonly name: string; readonly type: ApiKeyType; readonly environment: string; readonly permissions?: readonly string[]; readonly allowedCidrs?: readonly string[]; readonly expiresAt?: string; readonly cacheBehavior?: CacheBehavior;}ApiKeyCreateResponse
Section titled “ApiKeyCreateResponse”interface ApiKeyCreateResponse { readonly id: string; readonly rawSecret: string; // only returned once — must be stored by client readonly prefix: string; readonly lastFourChars: string; readonly name: string; readonly type: ApiKeyType; readonly environment: string; readonly expiresAt: string | null;}ApiKeyRotateResponse
Section titled “ApiKeyRotateResponse”interface ApiKeyRotateResponse { readonly newKeyId: string; readonly rawSecret: string; // only returned once readonly prefix: string; readonly lastFourChars: string; readonly oldKeyId: string;}ApiKeyUpdateScopesRequest
Section titled “ApiKeyUpdateScopesRequest”interface ApiKeyUpdateScopesRequest { readonly permissions: readonly string[]; readonly allowedCidrs: readonly string[];}React bindings
Section titled “React bindings”useKeycloakInit(config)
Section titled “useKeycloakInit(config)”Initializes Keycloak with PKCE (S256), performs check-sso, and manages the full session lifecycle.
function useKeycloakInit(config: KeycloakCoreConfig): KeycloakCoreResult;Returned fields (extends BaseAuthContextType):
| Field | Type | Description |
|---|---|---|
keycloakRef | RefObject<Keycloak | null> | Direct Keycloak instance reference |
login(options?) | (LoginOptions?) => void | Redirect to Keycloak login |
logout(options?) | (LogoutOptions?) => void | Redirect to Keycloak logout |
register(options?) | (Omit<LoginOptions, 'action'>?) => void | Shortcut for login({ action: 'register' }) |
hasRealmRole(role) | (string) => boolean | Check realm-level role |
hasResourceRole(role, resource?) | (string, string?) => boolean | Check resource-level role |
isTokenExpired(minValidity?) | (number?) => boolean | Check if token expires within n seconds |
tokenParsed | Record<string, unknown> | undefined | Decoded JWT payload |
Automatic side effects:
- Registers Bearer token getter via
setTokenGetter()on@granit/api-client - Registers 401 handler via
setOnUnauthorized()— triggerskeycloak.logout() - Refreshes token every 60 seconds via
setInterval - Loads user info from
/userinfoendpoint (or fromtokenParsedwhenuseTokenClaims: true)
Session lifecycle
Section titled “Session lifecycle”| Event | Effect |
|---|---|
| Token expired | Auto-refresh handles it (60s interval) |
Refresh fails (onAuthRefreshError) | authenticated → false, forces logout |
Session revoked (onAuthLogout) | authenticated → false, user → null |
| HTTP 401 received | onUnauthorized callback triggers keycloak.logout() |
Back-channel logout (admin revokes session in Keycloak) is detected via the dual mechanism: the 60-second token refresh fails, or the next API call returns 401.
createAuthContext<T>()
Section titled “createAuthContext<T>()”Factory that creates a typed React context and useAuth hook. Each app calls this once
with its own extended context type.
function createAuthContext<T extends BaseAuthContextType>(): { AuthContext: React.Context<T | undefined>; useAuth: () => T;};useAuth() throws if called outside AuthContext.Provider.
createMockProvider<T>(AuthContext, value)
Section titled “createMockProvider<T>(AuthContext, value)”Creates a mock auth provider for Storybook and unit tests.
function createMockProvider<T extends BaseAuthContextType>( AuthContext: React.Context<T | undefined>, value: T): React.FC<{ children: React.ReactNode }>;const MockAuthProvider = createMockProvider(AuthContext, { keycloak: null, authenticated: true, loading: false, login: () => {}, logout: () => {},});API key hooks
Section titled “API key hooks”All hooks accept an ApiKeyHookOptions configuration:
interface ApiKeyHookOptions { client: AxiosInstance; basePath?: string; // default: '/api/v1/api-keys'}Query key factory
Section titled “Query key factory”const apiKeyKeys = { all: ['api-keys'] as const, lists: () => [...apiKeyKeys.all, 'list'] as const, list: (params) => [...apiKeyKeys.lists(), params] as const, details: () => [...apiKeyKeys.all, 'detail'] as const, detail: (id) => [...apiKeyKeys.details(), id] as const,};useApiKeys(params?, options)
Section titled “useApiKeys(params?, options)”Fetches a paginated, filterable list of API keys.
interface UseApiKeysParams { search?: string; type?: string[]; environment?: string; includeRevoked?: boolean; page?: number; pageSize?: number;}
function useApiKeys( params?: UseApiKeysParams, options: ApiKeyHookOptions): UseQueryResult<ApiKeyResponse[]>;useApiKey(id, options)
Section titled “useApiKey(id, options)”Fetches a single API key by ID. Query is enabled only when id is truthy.
function useApiKey(id: string, options: ApiKeyHookOptions): UseQueryResult<ApiKeyResponse>;useCreateApiKey(options)
Section titled “useCreateApiKey(options)”Creates a new API key. Returns ApiKeyCreateResponse with rawSecret — only available once.
function useCreateApiKey( options: ApiKeyHookOptions): UseMutationResult<ApiKeyCreateResponse, Error, ApiKeyCreateRequest>;useRevokeApiKey(options)
Section titled “useRevokeApiKey(options)”Revokes an API key. Invalidates list and detail caches on success.
function useRevokeApiKey( options: ApiKeyHookOptions): UseMutationResult<void, Error, string>;useRotateApiKey(options)
Section titled “useRotateApiKey(options)”Rotates an API key — creates a new secret and invalidates the old one.
function useRotateApiKey( options: ApiKeyHookOptions): UseMutationResult<ApiKeyRotateResponse, Error, string>;useUpdateApiKeyScopes(options)
Section titled “useUpdateApiKeyScopes(options)”Updates permissions and allowed CIDRs on an existing key.
interface UpdateApiKeyScopesVariables { id: string; request: ApiKeyUpdateScopesRequest;}
function useUpdateApiKeyScopes( options: ApiKeyHookOptions): UseMutationResult<void, Error, UpdateApiKeyScopesVariables>;Public API summary
Section titled “Public API summary”| Category | Key exports | Package |
|---|---|---|
| OIDC types | KeycloakUserInfo, KeycloakEvent, LoginOptions, LogoutOptions | @granit/authentication |
| Auth context | BaseAuthContextType, KeycloakCoreConfig | @granit/authentication |
| Keycloak hook | useKeycloakInit(), KeycloakCoreResult | @granit/react-authentication |
| Context factory | createAuthContext(), createMockProvider() | @granit/react-authentication |
| API key types | ApiKeyResponse, ApiKeyCreateRequest, ApiKeyCreateResponse, ApiKeyRotateResponse | @granit/authentication-api-keys |
| API key hooks | useApiKeys(), useApiKey(), useCreateApiKey(), useRevokeApiKey(), useRotateApiKey() | @granit/react-authentication-api-keys |
| Query keys | apiKeyKeys | @granit/react-authentication-api-keys |
See also
Section titled “See also”- Granit.Security module — .NET authentication and JWT Bearer middleware
- API client —
setTokenGetterandsetOnUnauthorizedare wired automatically - Authorization — Permission checking after authentication
- Multi-tenancy — Tenant resolution from JWT claims