SUPER EARLY WIP — USE AT YOUR OWN RISK
shoo
GitHubCOMING SOON
API Reference

@shoojs/auth

Vanilla browser client for Shoo authentication

Install

bun add @shoojs/auth

Framework-agnostic. Works in any browser environment. If you're using React, see @shoojs/react instead.

createShooAuth(options)

Creates a Shoo auth client instance. This is the main entry point.

import { createShooAuth } from "@shoojs/auth";

const auth = createShooAuth({
  shooBaseUrl: "https://shoo.dev",
});

ShooAuthOptions

OptionTypeDefaultDescription
shooBaseUrlstring"https://shoo.dev"Shoo server URL
callbackPathstring"/auth/callback"Path Shoo redirects to after auth
redirectUristringAuto-derived from callbackPathFull callback URL
clientIdstring"origin:{your_origin}"Auto-derived from redirect URI origin
requestPiibooleanfalseRequest email/name/picture by default
storageKeystring"shoo_identity"localStorage key for identity
pkceStorageKeystring"shoo_pkce"sessionStorage key for PKCE bundle
returnToStorageKeystring"shoo_return_to"sessionStorage key for return URL
fallbackPathstring"/"Redirect target when no return-to is stored

Returns a ShooAuthClient.


ShooAuthClient

client.startSignIn(params?)

Generates a PKCE bundle, stores the verifier in sessionStorage, and redirects the browser to the Shoo authorize endpoint.

await auth.startSignIn();

// With options
await auth.startSignIn({
  requestPii: true,
  returnTo: "/dashboard",
});

StartSignInOptions:

OptionTypeDescription
requestPiibooleanOverride default PII setting
returnTostringURL to redirect to after sign-in
redirectUristringOverride callback URL
clientIdstringOverride client ID
shooBaseUrlstringOverride Shoo server URL

Returns Promise<{ url: string; bundle: PkceBundle }>.

client.handleCallback(params?)

The high-level callback handler. Exchanges the authorization code, persists identity, and redirects to the stored return-to URL.

const token = await auth.handleCallback();
// Browser redirects after this call

Use this in your callback page. It handles everything: code exchange, identity persistence, URL cleanup, and redirection.

HandleCallbackOptions:

OptionTypeDescription
urlstringURL to parse (defaults to window.location.href)
redirectTostringExplicit redirect target (overrides stored return-to)
returnTostringAlias for redirectTo
fallbackPathstringFallback if no return-to is stored

Returns Promise<TokenResponse | null>. Returns null if no callback params are present.

client.finishSignIn(params?)

Lower-level code exchange. Unlike handleCallback, this does not automatically redirect. Use this when you need more control.

const token = await auth.finishSignIn({
  redirectAfter: false,
});

FinishSignInOptions:

OptionTypeDefaultDescription
urlstringwindow.location.hrefURL to parse
clearCallbackParamsbooleantrueRemove code/state from URL bar
redirectAfterbooleanfalseRedirect to return-to URL
consumeReturnTobooleantrueRemove stored return-to after reading
redirectTostringExplicit redirect target
returnTostringAlias for redirectTo
fallbackPathstring"/"Fallback path
redirectUristringOverride callback URL
clientIdstringOverride client ID
shooBaseUrlstringOverride Shoo server URL

Returns Promise<TokenResponse | null>.

client.getIdentity(storageKey?)

Reads the persisted identity from localStorage.

const identity = auth.getIdentity();
// { userId: "ps_a1B2...", token: "eyJ...", expiresIn: 300, receivedAt: 1234567890 }

Returns ShooIdentity:

PropertyTypeDescription
userIdstring | nullThe user's domain-scoped ID, or null if signed out
tokenstring | undefinedThe raw id_token JWT
expiresInnumber | undefinedToken lifetime in seconds
receivedAtnumber | undefinedTimestamp when the token was stored

client.persistIdentity(userId, token, storageKey?, extras?)

Writes identity to localStorage. Called automatically by handleCallback and finishSignIn.

client.clearIdentity(storageKey?)

Removes the persisted identity from localStorage. Use this for sign-out.

auth.clearIdentity();

client.decodeIdentityClaims(idToken?)

Decodes the JWT payload without verifying the signature. For display purposes only — always verify tokens on your server.

const claims = auth.decodeIdentityClaims();
// { iss, aud, sub, pairwise_sub, iat, exp, jti, email?, name?, picture? }

Returns IdentityClaims | null.

client.exchangeCode(params)

Exchanges an authorization code for an id_token via POST /token.

const token = await auth.exchangeCode({
  shooBaseUrl: "https://shoo.dev",
  clientId: "origin:https://myapp.com",
  redirectUri: "https://myapp.com/auth/callback",
  code: "the_auth_code",
  codeVerifier: "the_pkce_verifier",
});

Returns Promise<TokenResponse>:

PropertyTypeDescription
token_type"Bearer"Always "Bearer"
expires_innumberToken lifetime in seconds
pairwise_substringDomain-scoped user ID
id_tokenstringSigned JWT

client.checkSession(params?)

Checks whether the current token/session is still valid with Shoo (including revocation).

const result = await auth.checkSession();

if (result.status === "login_required") {
  auth.clearIdentity();
}

Returns:

  • { status: "active" }
  • { status: "login_required", reason: "revoked" | "expired" | "invalid_token" }
  • { status: "unsupported" } when connected to an older Shoo server that does not expose /session/check yet.

client.startSessionMonitor(options?)

Starts periodic background checks using checkSession.

const monitor = auth.startSessionMonitor({
  intervalMs: 60_000,
  onLoginRequired: () => auth.clearIdentity(),
});

// later
monitor.stop();

SessionMonitorOptions:

OptionTypeDefaultDescription
intervalMsnumber60000Check cadence
immediatebooleantrueRun one check immediately
onLoginRequired(result) => voidCalled on revoked/expired/invalid token
onError(error) => voidCalled on network/server errors
tokenstringStored identity tokenOverride token source
storageKeystring"shoo_identity"localStorage identity key
clientIdstringDerived from redirect URIOptional audience guard
redirectUristringClient default redirect URIUsed when deriving clientId
shooBaseUrlstringClient default shooBaseUrlShoo API origin

client.createPkceBundle()

Generates a PKCE bundle with a random state, verifier, and S256 challenge.

const bundle = await auth.createPkceBundle();
// { state: "...", verifier: "...", challenge: "..." }

client.createSignInUrl(params)

Builds an authorize URL without navigating. Useful for custom sign-in flows.

const url = auth.createSignInUrl({
  state: bundle.state,
  codeChallenge: bundle.challenge,
});

client.parseCallback(url?)

Extracts code and state from a callback URL. Returns null if no callback params are present.

client.clearCallbackParams()

Removes code, state, and error from the browser URL bar using history.replaceState.


Standalone functions

These are also exported at the top level for use without creating a client:

import {
  createPkceBundle,
  createSignInUrl,
  parseCallback,
  clearCallbackParams,
  getIdentity,
  persistIdentity,
  clearIdentity,
  decodeIdentityClaims,
  exchangeCode,
  checkSession,
  startSessionMonitor,
  deriveClientIdFromRedirectUri,
} from "@shoojs/auth";

deriveClientIdFromRedirectUri(redirectUri)

Computes the client ID from a redirect URI: "origin:" + new URL(redirectUri).origin.


Types

All types are exported from @shoojs/auth:

import type {
  PkceBundle,
  ShooIdentity,
  ShooAuthOptions,
  StartSignInOptions,
  ExchangeCodeParams,
  CheckSessionOptions,
  SessionCheckResult,
  SessionMonitorOptions,
  SessionMonitorHandle,
  FinishSignInOptions,
  HandleCallbackOptions,
  ShooAuthClient,
} from "@shoojs/auth";

IdentityClaims and TokenResponse are re-exported from @shoojs/types.