@plasius/auth


Authentication helpers and React context components for Plasius applications.
Apache-2.0. ESM + CJS builds. TypeScript types included.
Requirements
- Node.js 24+ (matches
.nvmrc)
- React 19 (
peerDependencies)
- Browser runtime for hooks (
window/document are used)
Installation
npm install @plasius/auth
Exports
import {
AuthProvider,
useAuth,
useAuthorizedFetch,
createAuthorizedFetch,
useLogin,
useLogout,
} from "@plasius/auth";
Quick Start
import { AuthProvider, useAuth, useLogin, useLogout } from "@plasius/auth";
import type { AuthProvider as OAuthProviderId } from "@plasius/entity-manager";
function AccountPanel() {
const { userId, validateSession } = useAuth();
const login = useLogin();
const logout = useLogout();
const provider = "github" as OAuthProviderId;
return (
<div>
<p>Signed in as: {userId ?? "anonymous"}</p>
<button onClick={() => login(provider)}>Log in</button>
<button onClick={() => logout()}>Log out</button>
<button onClick={() => validateSession()}>Revalidate session</button>
</div>
);
}
export default function App() {
return (
<AuthProvider>
<AccountPanel />
</AuthProvider>
);
}
API
AuthProvider
Provides auth state through context and runs session validation on mount.
useAuth()
Returns:
userId: string | null
setUserId(userId: string | null)
validateSession(): Promise<void>
validateSession() calls GET /oauth/me and updates userId from a userId field in the response body.
useAuthorizedFetch()
React hook that returns an authorized fetch wrapper.
createAuthorizedFetch()
Non-hook function that creates the same authorized fetch wrapper.
Behavior:
- Always sends requests with
credentials: "include".
- Reads
csrf-token from browser cookies and sends it as x-csrf-token when present.
- On
401, calls POST /oauth/refresh-token and retries the original request.
- Deduplicates concurrent refresh calls with a shared promise.
- Limits refresh to one retry cycle per request (prevents recursive retry loops).
- Applies cooldown after refresh failure or repeated
401 so clients return failure instead of repeatedly hitting auth endpoints.
- For outage responses (
429/5xx) on refresh, uses randomized logarithmic backoff with an increasing cooldown window.
- Honors
Retry-After (seconds) from refresh responses before retrying.
useLogin()
Returns a function that redirects to:
/oauth/{provider}?state={base64(currentPath)}
The provider identifier type comes from @plasius/entity-manager (AuthProvider), and is separate from this package's React AuthProvider component.
useLogout()
Returns a function that:
- Sends
POST /oauth/logout.
- Redirects the browser to
/ regardless of request outcome.
Server Integration Guide
This package is frontend-only. It assumes your backend owns authentication and issues cookies.
End-to-End Flow
- User clicks login and
useLogin() redirects browser to GET /oauth/{provider}?state={base64(path)}.
- Backend starts OAuth with the provider, completes callback handling, then sets auth cookies.
AuthProvider calls GET /oauth/me on mount to populate userId.
- API calls through
useAuthorizedFetch() include cookies and optional x-csrf-token.
- If a protected call returns
401, package sends POST /oauth/refresh-token once for concurrent callers.
- On successful refresh, original request is retried automatically.
useLogout() sends POST /oauth/logout then redirects to /.
Required API Contract
/oauth/{provider} | GET | useLogin() | Start provider login flow; accept state query param. |
/oauth/me | GET | AuthProvider.validateSession() | Return 200 with JSON containing userId when authenticated, otherwise non-2xx (typically 401). |
/oauth/refresh-token | POST | createAuthorizedFetch() after 401 | Attempt token/session refresh using cookies; return 2xx on success, non-2xx on failure. |
/oauth/logout | POST | useLogout() | Invalidate session cookies/server session and return 2xx/204 when possible. |
Endpoint Response Shapes
GET /oauth/me success example:
{
"userId": "user_123"
}
GET /oauth/me should return 401 (or another non-2xx) when no valid session exists.
POST /oauth/refresh-token may optionally return a Retry-After response header (seconds).
If present and greater than zero, the package waits before retrying the original request.
Cookies, CSRF, and Headers
- Auth/session cookie must be sent on credentialed requests (
credentials: "include" is always used).
- For CSRF protection, expose a readable cookie named
csrf-token if you want the package to send x-csrf-token.
useAuthorizedFetch() adds x-csrf-token only when the csrf-token cookie exists.
- Refresh calls to
/oauth/refresh-token do not include x-csrf-token automatically; protect this route with cookie policy and origin checks.
useLogout() uses authorized fetch, so logout receives x-csrf-token when available.
Cross-Origin Deployment (If API and App Origins Differ)
Configure backend CORS and cookies for credentialed requests:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin must be a specific origin, not *
- Session cookies should use
Secure and an appropriate SameSite policy for your topology.
Security Notes for /oauth/{provider} state
state is set by the client as base64(window.location.pathname). Backend should:
- Treat
state as untrusted input.
- Validate/decode safely.
- Restrict post-login redirects to allowed in-app paths to prevent open redirects.
Minimal Backend Checklist
- Implement all four routes above.
- Issue and clear session cookies reliably.
- Return
userId from /oauth/me for authenticated sessions.
- Return
401 for expired/invalid sessions.
- Make
/oauth/refresh-token idempotent and safe for concurrent requests.
- Enforce CSRF/origin protections for state-changing endpoints.
OAuth 2.0 Standards Alignment
This package is designed to align with OAuth 2.0 and current IETF security guidance when paired with a compliant backend.
Core Standards
Browser-Based App Guidance
Practical Alignment Notes for This Package
- The frontend uses a server-backed session model (cookie-based), which helps avoid exposing long-lived OAuth tokens to browser JavaScript.
- The backend should use Authorization Code + PKCE with the identity provider.
- The backend should enforce exact redirect URI matching and reject open redirects.
- The backend should treat the incoming
state value as untrusted input.
- The backend should generate and validate its own CSRF correlation/anti-forgery value for OAuth redirects and state-changing endpoints.
- The backend should revoke or invalidate tokens/sessions during logout.
- The backend should use HTTPS everywhere and secure cookie settings (
Secure, HttpOnly, SameSite aligned to deployment topology).
Compliance Checklist (Backend)
- Do not use Implicit Grant or Resource Owner Password Credentials flows.
- Use Authorization Code grant with PKCE for user login.
- Validate redirect URIs exactly against registered values.
- Protect state-changing endpoints (
/oauth/logout, /oauth/refresh-token) against CSRF.
- Avoid putting access tokens in URL query parameters.
- Return non-2xx for invalid sessions and avoid leaking sensitive error detail.
Build Outputs
The package publishes:
- ESM bundle:
dist/index.js
- CJS bundle:
dist/index.cjs
- Type definitions:
dist/index.d.ts
Development
npm run clean
npm run build
npm test
npm run test:coverage
npm run lint
Demo scaffold:
npm run build
node demo/example.mjs
Release Policy
Package publishing is performed through GitHub CD workflows only. Do not publish directly from local machines.
Contributing