Documentation Menu
Client SDK
The OpenAuthsterClient class (from openauthster-shared/client/user) is the recommended way to integrate authentication into your application. It manages the full lifecycle: PKCE login flow, token storage, automatic refresh, session read/write, and authenticated requests.
Creating a Client
import { createOpenAuthsterClient } from "openauthster-shared/client/user";
const client = createOpenAuthsterClient({
clientID: "my_project",
issuerURI: "https://randomUUID-auth.yourdomain.com",
redirectURI: "https://myapp.com/",
copyID: "en-us", // copy template for i18n (null if unused)
secret: undefined, // server-side only
});Options Reference
| Option | Type | Required | Description |
|---|---|---|---|
clientID | string | Yes | Project slug from the Web UI |
issuerURI | string | Yes | Base URL of the OpenAuthster issuer generated during the project creation |
redirectURI | string | Yes | URL the issuer redirects to after login |
copyID | string | null | No | Copy template ID for i18n (e.g. "en-us") |
secret | string | No | Client secret — server-side only, needed for private sessions |
token | string | null | No | Pre-existing access token |
refreshToken | string | null | No | Pre-existing refresh token |
subject | SubjectSchema | No | Custom subject schema for token verification (defaults to built-in) |
Custom subject schema (0.2.0)
The subject should be set both in the issuer and client to work.
Issuer:openauth.config.ts
import { createSubjects } from "@openauthjs/openauth/subject";
const subject = createSubjects({
user: v.object({
id: v.string(),
email: v.string(),
role: v.union([v.literal("admin"), v.literal("user")]),
}),
});
const client = createOpenAuthsterClient({
clientID: "my_project",
issuerURI: "https://auth.yourdomain.com",
redirectURI: "https://myapp.com/",
subject,
});Initialisation
init()
Call once in the browser on page load:
await client.init();This method:
- Checks if a
codequery parameter exists (OAuth callback). - Check if a
invite_idquery parameter exists (invite flow). - If so, exchanges it for tokens using the stored PKCE challenge.
- Otherwise, restores tokens from
localStorage. - Fires all registered initialisation listeners.
callback() (manual)
If you handle the OAuth redirect on a custom route, call await client.callback() to process the exchange and clean the URL query params. The logic mirrors what init() does automatically.
Authentication
login()
Redirects the user to the issuer's login page:
await client.login();Under the hood this calls client.authorize(redirectURI, "code"), stores a PKCE challenge in localStorage, and sets window.location.href.
logout()
Clears all tokens and session data:
client.logout();After calling logout():
isAuthenticatedbecomesfalsedata.publicanddata.privateare reset to{}- Tokens are removed from
localStorage - The refresh timer is cancelled
Auth State
client.isAuthenticated; // boolean — true once tokens are obtained
client.isLoaded; // boolean — true after init() completes
client.expiresIn; // number | undefined — seconds until access token expires
client.userMeta; // { user_id: string | null, user_identifier: string | null }
client.userInfo; // provider user info (e.g. { provider: "google" })
client.error; // { error: string; error_description: string | null } | nullInitialization listeners now receive an optional error parameter: (client, error?) => void. Handle errors there to surface failed callbacks in your UI.
Authenticated Fetch
client.fetch() works like the global fetch() but adds auth headers automatically:
const res = await client.fetch("/api/v1/profile");
const data = await res.json();Headers added:
Authorization: Bearer <accessToken>X-Client-Secret: <secret>(only when a secret is configured)
Token helpers (0.2.0)
getToken()— read the current access token (restores fromlocalStoragewhen needed).setTokenToCookie()— store the token in a secure cookie (path=/; secure; samesite=strict;).verify(token?)— validate a token against the configured subject schema (falls back to the current token when omitted).
Auto Token Refresh
The client schedules a silent refresh 60 seconds before the access token expires. New tokens are persisted to localStorage automatically. If the refresh fails, a warning is logged to the console.
Listeners
Register callbacks that fire when auth state changes:
client.addInitializationListener("my-key", (client, error) => {
console.log("Auth state changed:", client.isAuthenticated);
});Call triggerUpdate() manually after state-changing operations like updateUserSession():
await client.updateUserSession("public", { theme: "dark" });
client.triggerUpdate(); // listeners fireUser management (admin, server-only, 0.2.0)
These helpers require a secret and should only run server-side:
getUserById(user_id)— fetch a specific usergetUsers({ page, limit })— paginated list of usersupdateUserById(user_id, data)— overwrite identifier/public/private fieldsdeleteUserById(user_id)— delete the user
All methods perform schema validation and return Error on failure.
Updating Copy Template
Switch the i18n copy template at runtime:
client.updateOptions({ copyID: "fr-fr" });This recreates the internal OpenAuth client with the new template.
Low-Level Client
If you only need the raw @openauthjs/openauth client with OpenAuthster cookies, import from openauthster-shared/client:
import { createClient, createServerClient } from "openauthster-shared/client";
const rawClient = createClient({
clientID: "my_project",
issuer: "https://auth.yourdomain.com",
copyID: "en-us",
});This returns a plain OpenAuth Client — you handle tokens, storage, and refreshing yourself.