@usu-accessibility/auth-service-handler
v1.0.25
Published
A modern TypeScript package for handling authentication services.
Downloads
681
Maintainers
Readme
@usu-accessibility/auth-service-handler
A modern TypeScript package for handling authentication services across servers (Hono/Fetch) and clients (React).
- Handles persisting sessions via HTTP-only cookies.
- Provides helpers to read the current session in server code.
- Exposes a simple way to construct a login URL (with optional redirect support).
- Includes React hooks to read the session and logout from the browser.
- Server helpers to fetch users and roles; create, update, and delete roles; and create, update, or delete users.
Installation
npm install @usu-accessibility/auth-service-handlerConfiguration
You can configure the library via environment variables or by passing options to createAuthHandler().
Required values:
AUTH_APP_IDAUTH_APP_SECRETAUTH_ON_SUCCESS_REDIRECT_URLAUTH_ON_ERROR_REDIRECT_URLAUTH_SERVER_URL
You may also pass these as options directly to createAuthHandler(options); any option provided will override the corresponding environment variable.
API Reference
createAuthHandler(options?)
Creates an auth helper bound to your app configuration.
Signature: createAuthHandler(options?: { appId?, appSecret?, onSuccessRedirectUrl?, onErrorRedirectUrl?, authServerUrl? })
Returns:
getServerSession(headers: Headers) => Promise<Session | null>— Server helper to read and validate the current session from the incoming request headers.getLoginData(options?: { redirectUrl?: string }) => { url: string }— Returns the login URL to start the SAML flow. Optionally appends aredirectUrlquery param to control post-login redirection.getUsers(opts: { aNumbers?: string[]; roles?: string[] }) => Promise<Session["user"][]>— Fetches user profiles filtered by A-numbers and/or roles using your auth server.getRoles() => Promise<Role[]>— Fetches all available roles from your auth server.createRole(name: string) => Promise<Role>— Creates a new role on your auth server.createUser(params: { aNumber: string; name?: string; email?: string; roleId?: string }) => Promise<Session["user"] & { createdAt: string; updatedAt: string; isInitialized: boolean }>— Creates a new user on your auth server.deleteUser(aNumber: string) => Promise<true>— Deletes a user (by A-number) on your auth server.updateUserRole(aNumber: string, roleId: string) => Promise<true>— Updates a user's role on your auth server.deleteRole(roleId: string) => Promise<true>— Deletes a role on your auth server.updateRole(roleId: string, name: string) => Promise<true>— Renames/updates a role on your auth server.options: AuthOptions— The resolved configuration used by this handler.
Example:
import { createAuthHandler } from "@usu-accessibility/auth-service-handler";
const auth = createAuthHandler({
appId: process.env.AUTH_APP_ID,
appSecret: process.env.AUTH_APP_SECRET,
onSuccessRedirectUrl: process.env.AUTH_ON_SUCCESS_REDIRECT_URL,
onErrorRedirectUrl: process.env.AUTH_ON_ERROR_REDIRECT_URL,
authServerUrl: process.env.AUTH_SERVER_URL,
});
// Server-side session
const session = await auth.getServerSession(request.headers);
// Create login URL
const { url: loginUrl } = auth.getLoginData({
redirectUrl: "https://your.app/after-login",
});Server Handlers
The library provides two platform-focused handlers you can mount on your server. Each handler expects to be mounted on a base route (e.g., /auth) and uses the last path segment to switch behavior:
POST/GET /auth/set?session=...&redirectUrl=...— Sets the session cookie and redirects toredirectUrlif provided, otherwise toAUTH_ON_SUCCESS_REDIRECT_URL.GET /auth/session— Returns the currentSessionas JSON when authenticated. On missing/invalid session: Fetch handler responds401; Hono handler responds200withnull.POST /auth/logout— Logs the user out, deletes the cookie, and redirects to/.GET /auth/login-url?redirectUrl=/path— Returns{ url: string }JSON for initiating login. TheredirectUrlis appended only if it starts with/.
Exports and imports:
- Fetch-compatible handler:
import { fetchHandler } from "@usu-accessibility/auth-service-handler/handlers/fetch" - Hono handler:
import { honoHandler } from "@usu-accessibility/auth-service-handler/handlers/hono"
Both handlers are factories: call them with your resolved AuthOptions to get a request handler.
Hono example
import { Hono } from "hono";
import { createAuthHandler } from "@usu-accessibility/auth-service-handler";
import { honoHandler } from "@usu-accessibility/auth-service-handler/handlers/hono";
const app = new Hono();
const auth = createAuthHandler();
// Mount once and let the handler switch by path suffix
app.get("/auth/*", honoHandler(auth.options));
app.post("/auth/*", honoHandler(auth.options));
// Usage examples:
// - GET /auth/session -> returns JSON session
// - POST /auth/logout -> logs out and redirects
// - GET /auth/set?session=token&redirectUrl=https://your.app/after
// - GET /auth/login-url?redirectUrl=/after -> returns JSON { url }Fetch (standard Web Request/Response) example
This works in environments with the standard Fetch API (e.g., Cloudflare Workers, Next.js Route Handlers, Bun, Deno, or Node 18+ with Fetch).
import { createAuthHandler } from "@usu-accessibility/auth-service-handler";
import { fetchHandler } from "@usu-accessibility/auth-service-handler/handlers/fetch";
const auth = createAuthHandler();
const handleAuth = fetchHandler(auth.options);
export async function GET(req: Request) {
// Mount at /auth/* and forward requests here
return handleAuth(req);
}
export async function POST(req: Request) {
return handleAuth(req);
}
// Examples once mounted under /auth/*:
// GET /auth/session
// POST /auth/logout
// GET /auth/set?session=token&redirectUrl=https://your.app/after
// GET /auth/login-url?redirectUrl=/afterNote: If you’re using Express without a Fetch-compatible adapter, you’ll need a small shim to convert Express’s
req/restoRequest/Response. Many community adapters exist; otherwise consider Hono which runs on Node and many other runtimes.
Server Helpers
getServerSession(authOptions)
Signature: (headers: Headers) => Promise<Session | null>
Reads the Cookie header, extracts the session token, validates it against your auth server, and returns a Session object or null.
Usage via createAuthHandler():
const auth = createAuthHandler();
const session = await auth.getServerSession(request.headers);
if (!session) {
// handle unauthorized
}getLoginData(authOptions)
Signature: (options?: { redirectUrl?: string }) => { url: string }
Returns the login URL for starting the SAML auth flow. If you provide redirectUrl, it will be appended as a query parameter so your app can redirect the user post-login.
Usage via createAuthHandler():
const auth = createAuthHandler();
const { url: loginUrl } = auth.getLoginData({
redirectUrl: "/after_login",
});
// Redirect the user to loginUrlgetUsers(authOptions)
Signature: (opts: { aNumbers?: string[]; roles?: string[] }) => Promise<Session["user"][]>
Fetches users from the auth server. Provide one or both filters:
aNumbers— Array of A-numbers to includeroles— Array of role names to include
Usage via createAuthHandler():
const auth = createAuthHandler();
const users = await auth.getUsers({
aNumbers: ["A12345678"],
roles: ["admin"],
});Returns: Promise<Session["user"][]>
getRoles(authOptions)
Signature: () => Promise<Role[]>
Fetches all roles from the auth server.
Usage via createAuthHandler():
const auth = createAuthHandler();
const roles = await auth.getRoles();createRole(authOptions)
Signature: (name: string) => Promise<Role>
Creates a new role on the auth server.
Usage via createAuthHandler():
const auth = createAuthHandler();
const role = await auth.createRole("editor");createUser(authOptions)
Signature: (params: { aNumber: string; name?: string; email?: string; roleId?: string }) => Promise<Session["user"] & { createdAt: string; updatedAt: string; isInitialized: boolean }>
Creates a new user on the auth server.
Usage via createAuthHandler():
const auth = createAuthHandler();
const user = await auth.createUser({
aNumber: "A12345678",
name: "Ada Lovelace",
email: "[email protected]",
roleId: "role-id-123",
});deleteUser(authOptions)
Signature: (aNumber: string) => Promise<true>
Deletes a user (by A-number) from the auth server.
Usage via createAuthHandler():
const auth = createAuthHandler();
await auth.deleteUser("A12345678");updateUserRole(authOptions)
Signature: (aNumber: string, roleId: string) => Promise<true>
Updates a user's role on the auth server.
Usage via createAuthHandler():
const auth = createAuthHandler();
await auth.updateUserRole("A12345678", "role-id-123");deleteRole(authOptions)
Signature: (roleId: string) => Promise<true>
Deletes a role from the auth server.
Usage via createAuthHandler():
const auth = createAuthHandler();
await auth.deleteRole("role-id-123");updateRole(authOptions)
Signature: (roleId: string, name: string) => Promise<true>
Renames/updates a role on the auth server.
Usage via createAuthHandler():
const auth = createAuthHandler();
await auth.updateRole("role-id-123", "Content Editor");React Hooks
Import from the React subpath:
import {
useSession,
useLogout,
useLoginUrl,
} from "@usu-accessibility/auth-service-handler/react";Common options:
{ authUrl?: string }— Override the base auth URL used by the hooks. Defaults to/api/auth(internalAUTH_URL). When using the provided handlers, set this to your mounted base (e.g.,/auth).
useSession(options?)
Signature: useSession(options?: { authUrl?: string })
Returns a union state of:
{ session: Session; loading: false; error: null }when authenticated{ session: null; loading: false; error: null }when unauthenticated{ session: null; loading: true; error: null }while loading{ session: null; loading: false; error: string }on error
Example:
function Profile() {
const state = useSession({ authUrl: "/auth" });
if (state.loading) return <p>Loading...</p>;
if (state.error) return <p>Error: {state.error}</p>;
if (!state.session) return <a href="/login">Login</a>;
return (
<div>Hello, {state.session.user.name ?? state.session.user.aNumber}</div>
);
}Note: By default, the hook fetches from
${authUrl}/session(so pass the base, e.g.,/auth).
When using the Fetch handler, unauthenticated requests return HTTP 401, so
useSessionwill produce the error state. With the Hono handler, unauthenticated requests return200withnull, souseSessionwill yield the unauthenticated state instead of an error.
useLogout(options?)
Signature: useLogout(options?: { authUrl?: string })
Returns { logout: () => Promise<{ success: true }>} . The logout function posts to ${authUrl}/logout and throws on non-OK responses.
Example:
function LogoutButton() {
const { logout } = useLogout({ authUrl: "/auth" });
return (
<button
onClick={async () => {
await logout();
window.location.href = "/";
}}
>
Logout
</button>
);
}useLoginUrl(options?)
Signature: useLoginUrl(options?: { authUrl?: string; redirectUrl?: string })
Returns { getLoginUrl: () => Promise<{ url: string }>} . The getLoginUrl function requests ${authUrl}/login-url and returns the constructed login URL. If redirectUrl is provided and starts with /, it will be included in the request.
Example:
function LoginLink() {
const { getLoginUrl } = useLoginUrl({
authUrl: "/auth",
redirectUrl: "/after",
});
return (
<button
onClick={async () => {
const { url } = await getLoginUrl();
window.location.href = url;
}}
>
Login
</button>
);
}Types
// Available via: import type { AuthOptions, Session, Role } from "@usu-accessibility/auth-service-handler"
export type AuthOptions = {
appId: string;
appSecret: string;
onSuccessRedirectUrl: string;
onErrorRedirectUrl: string;
authServerUrl: string;
};
export type Session = {
id: string;
user: {
aNumber: string;
name: string | null;
email: string | null;
avatarUrl: string;
role: {
id: string;
name: string;
};
};
};
export type Role = {
id: string;
name: string;
createdAt: string;
updatedAt: string;
};End-to-End Flow Summary
- Your app constructs a login URL via
auth.getLoginData({ redirectUrl })and redirects the user to it. - After authentication, your auth server redirects back to your app’s
/auth/set?session=...&redirectUrl=...endpoint. - The handler sets a secure cookie and redirects to the provided
redirectUrl(or the configuredonSuccessRedirectUrl). - Your app can now fetch
/auth/session(server or client) to retrieve the current userSession. - To sign out, POST
/auth/logoutand then redirect the user as desired.
Development
- Source:
src/ - Build:
dist/
License
ISC
