@plexusone/auth¶
Authentication primitives for secure BFF-based authentication.
Installation¶
Features¶
- BFF (Backend-for-Frontend) authentication pattern
- HTTP-only cookie session management
- Protected route component
- OAuth provider linking
- TypeScript-first API
Usage¶
AuthProvider¶
Wrap your application with AuthProvider:
import { AuthProvider } from '@plexusone/auth';
function App() {
return (
<AuthProvider bffBaseUrl="/api/auth">
<YourApp />
</AuthProvider>
);
}
useAuth Hook¶
Access authentication state and methods:
import { useAuth } from '@plexusone/auth';
function UserInfo() {
const { user, isAuthenticated, isLoading, login, logout } = useAuth();
if (isLoading) return <div>Loading...</div>;
if (!isAuthenticated) {
return <button onClick={() => login()}>Login</button>;
}
return (
<div>
<p>Welcome, {user.name}</p>
<button onClick={() => logout()}>Logout</button>
</div>
);
}
ProtectedRoute¶
Protect routes that require authentication:
import { Routes, Route } from 'react-router-dom';
import { ProtectedRoute } from '@plexusone/auth';
function AppRoutes() {
return (
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route element={<ProtectedRoute />}>
<Route path="/" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Route>
</Routes>
);
}
Linked Accounts¶
Manage OAuth provider connections:
import { useLinkedAccounts } from '@plexusone/auth';
function LinkedAccounts() {
const { accounts, link, unlink } = useLinkedAccounts();
return (
<div>
{accounts.map((account) => (
<div key={account.provider}>
{account.provider}: {account.connected ? 'Connected' : 'Not connected'}
<button
onClick={() => (account.connected ? unlink(account.provider) : link(account.provider))}
>
{account.connected ? 'Disconnect' : 'Connect'}
</button>
</div>
))}
</div>
);
}
Session over the BFF¶
SessionProvider reads GET /bff/session from the relying-party BFF and
useSession returns it: the person, their memberships keyed by
organization ID with a role per application, and the deployment's app
catalog. Sign-in is a redirect; sign-out is a same-origin POST.
import { SessionProvider, useSession, membershipFor } from '@plexusone/auth';
function Who({ org }: { org: string }) {
const { status, session, signIn, signOut } = useSession();
if (status === 'loading') return null;
if (status !== 'authenticated') return <button onClick={() => signIn()}>Sign in</button>;
const role = membershipFor(session, org)?.applications['reports'];
return (
<span>
{session.user?.name} ({role ?? 'no role'}){' '}
<button onClick={() => void signOut()}>Sign out</button>
</span>
);
}
<SessionProvider onUnauthorized={() => window.location.assign('/bff/auth/login')}>
<Who org={orgId} />
</SessionProvider>;
The session is re-read when the window regains focus and every five
minutes (refreshInterval, refreshOnFocus). onUnauthorized runs when a
signed-in session is found signed out, not after a deliberate sign-out.
API Reference¶
AuthProvider Props¶
| Prop | Type | Description |
|---|---|---|
bffBaseUrl |
string |
Base URL for BFF auth endpoints |
loginPath |
string |
Path to redirect for login (default: /login) |
onAuthError |
(error: Error) => void |
Error callback |
useAuth Return Value¶
| Property | Type | Description |
|---|---|---|
user |
User \| null |
Current user object |
isAuthenticated |
boolean |
Whether user is authenticated |
isLoading |
boolean |
Loading state |
login |
() => void |
Initiate login flow |
logout |
() => Promise<void> |
Logout user |
refresh |
() => Promise<void> |
Refresh session |