@otmc-sw/authenticator
v0.1.0
Published
TypeScript SDK for OTMC Authenticator (OAuth 2.0 + OpenID Connect) with optional React integration
Maintainers
Readme
@otmc/authenticator
TypeScript SDK for integrating OTMC Authenticator (OAuth 2.0 + OpenID Connect) into JavaScript/TypeScript and React applications.
- Authorization Code Flow with PKCE (S256) — no client secrets in the browser.
- Framework-independent core with an optional React integration.
- Token lifecycle management, authenticated
fetchhelper, and observable auth state.
Installation
npm install @otmc/authenticatorReact integration is an optional peer dependency (react >= 18).
Requirements
- A public OIDC client registered in OTMC Authenticator (Authorization Code + PKCE).
redirectUriandpostLogoutRedirectUrimust be registered/allowed by the IdP.- HTTPS in production.
Configuration
import { createAuthClient } from '@otmc/authenticator';
const auth = createAuthClient({
authority: 'https://authenticator.example.com',
clientId: 'my-app',
redirectUri: `${window.location.origin}/callback`,
postLogoutRedirectUri: window.location.origin,
scope: 'openid profile email',
// storage: 'memory' | 'session' | custom StorageAdapter (default: 'memory')
// silentRedirectUri: `${window.location.origin}/silent-callback.html`,
});authority is used for OIDC discovery (/.well-known/openid-configuration); endpoint URLs are resolved by oidc-client-ts from the discovery document — they are not hardcoded.
Vanilla JavaScript / TypeScript
// Start login (redirects to the IdP)
await auth.login();
// On the redirectUri page, exchange the code for tokens
await auth.handleCallback();
// Observable state
const unsubscribe = auth.subscribe((state) => {
console.log(state.isAuthenticated, state.user);
});
// User info
const user = auth.getUser();
// Access token (auto-refreshes when expired and possible)
const token = await auth.getAccessToken();
// Authenticated API request (adds `Authorization: Bearer …`, retries once on 401)
const res = await auth.fetch('https://api.example.com/me');
// Logout
await auth.logout();Detecting the callback page
import { hasCallbackParams } from '@otmc/authenticator';
if (hasCallbackParams()) {
await auth.handleCallback();
}Token storage
The default storage is in-memory — tokens are not persisted to localStorage/sessionStorage, which reduces XSS exposure. To opt into session persistence:
createAuthClient({ ...config, storage: 'session' });Or provide a custom adapter implementing getItem/setItem/removeItem.
React integration
import { AuthProvider, useAuth, ProtectedRoute } from '@otmc/authenticator/react';
function App() {
return (
<AuthProvider
config={{
authority: 'https://authenticator.example.com',
clientId: 'my-app',
redirectUri: `${window.location.origin}/callback`,
scope: 'openid profile email',
}}
>
<ProtectedRoute
fallback={<div>Loading…</div>}
unauthenticated={<LoginButton />}
>
<Dashboard />
</ProtectedRoute>
</AuthProvider>
);
}
function Dashboard() {
const { user, logout, getAccessToken, fetch } = useAuth();
return (
<div>
<h1>Welcome {user?.name}</h1>
<button onClick={logout}>Logout</button>
</div>
);
}AuthProvider creates a single AuthClient, subscribes to auth state, and automatically processes the redirect callback when OIDC parameters are present (autoHandleCallback, enabled by default).
useAuth()
Returns { user, isLoading, isAuthenticated, error, state, client, login, logout, handleCallback, getAccessToken, refreshToken, fetch }.
ProtectedRoute
Props: children, fallback (shown while loading), unauthenticated (shown when not authenticated).
Route protection is a UX convenience only — backend APIs must independently validate access tokens and enforce authorization.
Error handling
All SDK errors are instances of AuthError (with optional code/cause). Auth state errors are surfaced through state.error / useAuth().error.
Security notes
- Authorization Code Flow + PKCE S256 only; no implicit flow, no client secrets.
- OAuth
stateand OIDCnonceare validated byoidc-client-ts. - Default in-memory token storage; avoid persisting tokens in
localStorage. auth.fetchattaches the bearer token without overwriting caller headers and retries at most once after a refresh.
Development
npm install
npm run build # tsc → dist/
npm test # vitest
npm run typecheckProject layout
packages/authenticator/
├── src/
│ ├── core/ # AuthClient, config, state, errors, public types
│ ├── oidc/ # oidc-client-ts wrapper + discovery helper
│ ├── token/ # TokenManager + configurable TokenStorage
│ ├── session/ # SessionManager (OIDC user → AuthState)
│ ├── api/ # authenticated fetch helper
│ ├── react/ # AuthProvider, useAuth, ProtectedRoute
│ └── index.ts # core entry point
├── examples/ # vanilla + react examples
└── tests/