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

OptionTypeRequiredDescription
clientIDstringYesProject slug from the Web UI
issuerURIstringYesBase URL of the OpenAuthster issuer generated during the project creation
redirectURIstringYesURL the issuer redirects to after login
copyIDstring | nullNoCopy template ID for i18n (e.g. "en-us")
secretstringNoClient secret — server-side only, needed for private sessions
tokenstring | nullNoPre-existing access token
refreshTokenstring | nullNoPre-existing refresh token
subjectSubjectSchemaNoCustom 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:

  1. Checks if a code query parameter exists (OAuth callback).
  2. Check if a invite_id query parameter exists (invite flow).
  3. If so, exchanges it for tokens using the stored PKCE challenge.
  4. Otherwise, restores tokens from localStorage.
  5. 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():

  • isAuthenticated becomes false
  • data.public and data.private are 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 } | null

Initialization 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 from localStorage when 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 fire

User management (admin, server-only, 0.2.0)

These helpers require a secret and should only run server-side:

  • getUserById(user_id) — fetch a specific user
  • getUsers({ page, limit }) — paginated list of users
  • updateUserById(user_id, data) — overwrite identifier/public/private fields
  • deleteUserById(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.


Next Steps