Aller au contenu

@kiavi/kiavi-browser

Ce contenu n’est pas encore disponible dans votre langue.

Current version: 0.3.1

Browser SDK for Kiavi-hosted authentication. Use this in any frontend (React, Vue, Svelte, plain JS) when you need to sign users in against a Kiavi auth server and attach their access token to outgoing API calls.

This page mirrors the llm-docs.md that ships with the installed package, that file is the authoritative source. If anything here ever drifts, trust the package version.

Terminal window
pnpm add @kiavi/kiavi-browser

(Or npm install, yarn add, bun add, they all work.)

Then run the init command to install the version-pinned integration guide for AI coding agents into your repo:

Terminal window
npx kiavi-browser init

This writes .kiavi/kiavi-browser.md and adds a reference block to your AGENTS.md (or CLAUDE.md). Re-run on every upgrade. See AI agent docs for the full explanation.

Three things, in order:

  1. Create one KiaviClient instance and export it.
  2. Call authenticate() at app startup (or inside a route guard) to ensure the user is signed in. This redirects to the hosted login page if not.
  3. Call getAccessToken() before each API call and attach it as a Bearer token.
lib/auth.ts
import { KiaviClient } from '@kiavi/kiavi-browser'
export const auth = new KiaviClient('https://auth.your-domain.com')
// app entry / root route loader
import { auth } from './lib/auth'
const session = await auth.authenticate()
// If there's no session, authenticate() has already triggered a full-page
// redirect and this line is never reached. No need for null checks.
// any API call
import { auth } from './lib/auth'
const token = await auth.getAccessToken()
const res = await fetch('/api/things', {
headers: { Authorization: `Bearer ${token}` },
})
// sign out button
import { auth } from './lib/auth'
await auth.signOut() // clears local state and redirects to the hosted signout page

That is the full happy path. Do not implement anything beyond this unless you have a reason.

new KiaviClient('https://auth.your-domain.com', {
clientId: 'my-app',
locale: 'en',
authPolicy: 'passkey_preferred',
debug: true,
})
  • clientId?: string, OIDC client ID. Required for exchange-mode auth servers (cross-origin deployments, the most common case); unused in cookie mode. The client picks the right flow automatically from /.well-known/kiavi-auth.json. A missing clientId in exchange mode surfaces as an error from authenticate() when the redirect is built.
  • locale?: string, passed as lang (and ui_locales where applicable) on hosted-page redirects.
  • authPolicy?: 'passkey_required' | 'passkey_preferred', override the policy advertised by the server. Most callers should leave this unset and let getAuthPolicy() read it from the server.
  • debug?: boolean | KiaviDebugLogger, true enables the built-in console logger with sensitive fields redacted. A function receives structured KiaviDebugLogEntry objects so you can route logs into your own observability tooling.

authenticate(options?): Promise<KiaviSession>

Section titled “authenticate(options?): Promise<KiaviSession>”

Ensure a session exists. Resolves with the live session if one is already present, silently refreshable, or recovered from a ?code=&state= callback the constructor saw on page load. Otherwise redirects to login and the returned promise never resolves.

Call once at app/route entry. options.returnTo controls where the user lands after sign-in (defaults to the current URL minus any stale code params). options.replace: true uses location.replace instead of assignment.

Returns a valid JWT, refreshing silently if needed. Call it on every API request. Do not cache the result, it’s cheap and refresh is automatic.

Throws KiaviSessionExpiredError if the session can’t be recovered; catch that and call authenticate() to kick off the redirect flow.

Clears local state, revokes server-side (fire-and-forget), and (by default) redirects to the hosted signout page. options.returnTo controls where the browser lands after sign-out (defaults to the current origin). Pass { redirect: false } to clear local state without navigating.

getSession(): Promise<KiaviSession | null>

Section titled “getSession(): Promise<KiaviSession | null>”

Non-redirecting read of the current session. Awaits any in-flight code exchange, then attempts one silent refresh. Returns null if the user is not signed in. Use this when you need to check for a session without triggering a redirect (e.g. to render a signed-out landing view). Safe to call on the server: returns null without throwing.

Subscribe to session changes. Fires on every successful authenticate/refresh, every sign-out, and every failed refresh that clears the session. Does not fire synchronously on subscription, call getSession() first if you need the current state. Returns an unsubscribe function.

  • getAuthPolicy(), returns 'passkey_required' or 'passkey_preferred' for UI that adapts to the configured policy.
  • getProfile(): Promise<KiaviProfile | null>, returns the authenticated user’s social profile snapshot ({ name, picture, provider }) captured by the IdP at sign-in time. Returns null when the user is unauthenticated or signed in without a social provider (passkey-only, email-code-only). Kiavi does not refresh this data , call the provider’s API yourself if you need fresh values. Throws KiaviAuthError on non-2xx responses other than 401 (which surfaces as null).
  • setLocale(locale) / setDebugLogger(debug), update configuration at runtime.

Two account-management methods let you trigger email flows directly from your app. Both resolve to void on success and throw KiaviAuthError on any non-2xx response.

resendVerificationEmail({ email, callbackURL? })

Section titled “resendVerificationEmail({ email, callbackURL? })”

Unauthenticated, call this when a user reports they didn’t receive their verification email. The server responds with 200 regardless of whether the email exists (to avoid leaking account existence), so treat a resolved promise as “request accepted”, not as “email definitely sent”.

try {
await auth.resendVerificationEmail({
email: 'user@example.com',
callbackURL: window.location.href,
})
showToast('If we know that email, a verification link is on its way.')
} catch (err) {
if (err instanceof KiaviAuthError && err.code === 'rate_limited') {
showToast(`Please wait ${err.retryAfterSeconds ?? 30}s before trying again.`)
} else {
showToast('Something went wrong. Please try again.')
}
}

Requires an active session. The request is authenticated via the auth server’s session cookie (sent automatically with credentials: 'include').

The server’s behavior depends on whether the current email is verified:

  • Verified current email → A confirmation email is sent to the current address. The email only actually changes after the user clicks the link.
  • Unverified current email (if the auth server allows it) → The email is updated immediately and a verification email is sent to the new address.

Either way, the promise resolves to void and you should tell the user “check your inbox”.

try {
await auth.changeEmail({
newEmail: 'new@example.com',
callbackURL: window.location.href,
})
showToast('Check your inbox to confirm the change.')
} catch (err) {
if (err instanceof KiaviAuthError) {
showToast(err.message)
}
}

Thrown by resendVerificationEmail, changeEmail, and getProfile on failure. Fields:

  • code: KiaviAuthErrorCode, 'rate_limited' | 'invalid_request' | 'unauthenticated' | 'server_error' | 'network_error' | 'unknown'
  • status: number, HTTP status, or 0 for network errors / invalid input
  • retryAfterSeconds?: number, parsed from the Retry-After header on 429 responses
  • message: string, server-provided message when available, otherwise a generic description

Thrown by getAccessToken() when the session can’t be recovered. Catch and call authenticate() to start a new sign-in flow.

Thrown when a browser-only method is called during SSR. Use getSession() (which is SSR-safe) instead.

Exported from @kiavi/kiavi-browser:

  • KiaviClient, the class above
  • KiaviSession, { token: string, expiresAt: number | null, user: KiaviSessionUser | null }
  • KiaviSessionUser, { id: string, email: string, emailVerified?: boolean, phone?: string, phoneVerified?: boolean, name: string | null }
  • KiaviProfile, { name: string | null, picture: string | null, provider: string }, returned by getProfile()
  • KiaviAuthPolicy, 'passkey_required' | 'passkey_preferred'
  • KiaviClientOptions, KiaviAuthenticateOptions, KiaviSignOutOptions
  • KiaviAuthStateListener, (session: KiaviSession | null) => void
  • KiaviSessionExpiredError, KiaviBrowserOnlyError
  • KiaviAuthError, KiaviAuthErrorCode
  • KiaviResendVerificationEmailOptions, KiaviChangeEmailOptions
  • KiaviDebugLogger, KiaviDebugLogEntry

Do:

  • Create exactly one KiaviClient instance per app and export it as a module singleton.
  • Call authenticate() once at the top of your app or inside a route guard, then trust that the session exists.
  • Call getAccessToken() inline on every API call, it’s fast and handles refresh.
  • Pair this with @kiavi/kiavi-js on your backend to verify the JWTs this client produces.

Do not:

  • Do not manually parse ?code=...&state=... from the URL. The client handles it automatically in the constructor.
  • Do not cache or store the return value of getAccessToken() beyond the scope of a single request, tokens are short-lived and the client refreshes them for you.
  • Do not implement your own refresh loop, interval, or retry wrapper around getAccessToken(). It is already silent and automatic.
  • Do not write tokens to localStorage or cookies yourself. The client owns session storage.
  • Do not call authenticate() in a loop, call it once at entry, then use getAccessToken() for API calls and getSession() for non-redirecting reads.