Auth
Browser and React authentication helpers from @kizaki/sdk.
Browser-side helpers for login, signup, logout, session management, and OAuth. Calls the platform's /__auth/* JSON API. Sessions are HttpOnly cookies — no tokens are exposed to JavaScript.
The auth model is declared in Inspire via auth {}. The platform compiles that config into the app artifact and exposes the /__auth/* endpoints.
Import Paths
// Browser helpers (vanilla JS / any framework)
import { login, logout, getSession, getAuthConfig } from '@kizaki/sdk/auth';
// React hooks and components
import { useAuth, AuthGate } from '@kizaki/sdk/auth/react';
// Also re-exported from the browser entry point
import { login, logout } from '@kizaki/sdk/browser';Browser API
login(credentials)
Log in with email and password. Sets a session cookie on success.
const { user } = await login({ email: 'alice@example.com', password: '...' });
// user: { id, email, name }Throws AuthError with code: "invalid_credentials" on bad credentials.
Throws AuthError with code: "email_not_verified" until the verification link is used.
signup(credentials)
Create an account. Provide either name or firstName/lastName. Returns { user: null } until email verification is completed.
const { user } = await signup({
email: 'alice@example.com',
password: '...',
name: 'Alice Smith',
});
// user === null until the email verification link is usedThrows AuthError with code: "account_exists" if the email is taken.
Throws AuthError with code: "verification_unavailable" if verification email delivery fails; no account is created.
logout()
Clear the session cookie.
await logout();getSession()
Check the current session. Returns { user: null } if not authenticated.
const session = await getSession();
if (session.user) {
console.log(session.user.email);
}getAuthConfig()
Load the server-declared auth configuration for the current app. Use this to render only the providers and linking UI that the compiled auth {} block actually enables.
const auth = await getAuthConfig();
if (auth.providers.includes('google')) {
loginWithGoogle('/dashboard');
}refreshSession()
Force-refresh the session token.
const session = await refreshSession();loginWithGoogle(returnTo?)
Start a Google OAuth flow. Navigates the browser away. The user returns to returnTo (default: current page) after authenticating.
loginWithGoogle('/dashboard');loginWithGithub(returnTo?)
Start a GitHub OAuth flow. Same pattern as Google.
loginWithGithub();loginWithProvider(provider, returnTo?)
Start an OAuth flow for any supported provider.
loginWithProvider('google', '/dashboard');Only call provider-specific helpers for providers returned by getAuthConfig().
onAuthStateChange(listener)
Subscribe to auth state changes. Returns an unsubscribe function.
const unsub = onAuthStateChange((session) => {
console.log(session.user ? 'in' : 'out');
});
// later
unsub();getSessionSync()
Return the last known session without a network request.
const session = getSessionSync();forgotPassword(email)
Request a password reset. Always resolves — the server never reveals whether the email is registered.
await forgotPassword('alice@example.com');resetPassword(userId, code, newPassword)
Set a new password using a reset code from the password reset email.
await resetPassword(userId, code, 'newSecurePassword');listAuthMethods()
List authentication methods linked to the current account. Requires an active session.
const methods = await listAuthMethods();
// [{ type: "password" }, { type: "oauth", provider: "google", idpId: "...", externalUserId: "...", displayName: "alice@gmail.com" }]linkPassword(password)
Add a password to an OAuth-only account. Requires an active session.
await linkPassword('newSecurePassword');High-risk account change. Handle AuthError with code: "reauth_required" by prompting re-authentication.
linkProvider(provider, returnTo?)
Start an OAuth provider linking flow. Navigates to the OAuth authorize endpoint. After authentication, the account is linked and the user is redirected to returnTo.
linkProvider('github', '/settings/account');Requires a recent session. Stale sessions fail with reauth_required.
unlinkMethod(method)
Remove an authentication method. Only OAuth methods can be unlinked. Throws last_method if this is the user's only remaining method.
const methods = await listAuthMethods();
const github = methods.find(m => m.provider === 'github');
if (github) await unlinkMethod(github);Also a recent-auth operation. Expect AuthError with code: "reauth_required" on stale sessions.
AuthError
Error thrown by auth operations.
try {
await login({ email, password });
} catch (e) {
if (e instanceof AuthError) {
console.log(e.code); // "invalid_credentials"
console.log(e.status); // 401
}
}Common codes: invalid_credentials, email_not_verified, verification_unavailable, reauth_required, last_method.
React API
Requires React 18+ as a peer dependency.
useAuth()
Manages auth state. Fetches the session on mount and subscribes to changes.
function LoginPage() {
const { user, loading, error, login, loginWithGoogle, logout } = useAuth();
if (loading) return <p>Loading...</p>;
if (user) return <p>Hello {user.name} <button onClick={logout}>Log out</button></p>;
return (
<>
<button onClick={() => login({ email: '...', password: '...' })}>Log in</button>
<button onClick={() => loginWithGoogle()}>Google</button>
{error && <p>{error}</p>}
</>
);
}Returns:
| Field | Type | Description |
|---|---|---|
user | AuthUser | null | Current user or null |
loading | boolean | True while an auth operation is in flight |
error | string | null | Error code from last failed operation |
login | (creds) => Promise | Email/password login |
signup | (creds) => Promise | Create account and login |
logout | () => Promise | Clear session |
refresh | () => Promise | Force token refresh |
loginWithGoogle | (returnTo?) => void | Google OAuth redirect |
loginWithGithub | (returnTo?) => void | GitHub OAuth redirect |
loginWithProvider | (provider, returnTo?) => void | Any OAuth redirect |
AuthGate
Renders children when authenticated, fallback when not.
<AuthGate
fallback={<LoginPage />}
loading={<Spinner />}
>
<Dashboard />
</AuthGate>Props:
| Prop | Type | Description |
|---|---|---|
children | ReactNode | Shown when authenticated |
fallback | ReactNode | Shown when not authenticated |
loading | ReactNode | Shown while checking session (default: null) |
Types
interface AuthUser {
id: string;
email: string;
name: string;
}
interface AuthSession {
user: AuthUser | null;
}
interface LoginCredentials {
email: string;
password: string;
}
interface SignupCredentials {
email: string;
password: string;
firstName?: string;
lastName?: string;
name?: string;
}
type AuthProvider = 'email' | OAuthProvider;
type OAuthProvider = 'google' | 'github';
type AuthLinkingMode = 'manual' | 'disabled';
type AuthStateListener = (session: AuthSession) => void;
interface AuthConfig {
providers: AuthProvider[];
oauthProviders: OAuthProvider[];
linking: AuthLinkingMode;
sessionDurationSeconds: number;
}
interface AuthMethod {
type: 'password' | 'oauth';
provider?: string; // "google" | "github" (only for oauth)
idpId?: string; // needed for unlinkMethod
externalUserId?: string; // needed for unlinkMethod
displayName?: string;
}Error Codes
| Code | Status | Meaning |
|---|---|---|
invalid_credentials | 401 | Wrong email or password |
email_not_verified | 403 | Account must complete email verification first |
account_exists | 409 | Email already registered (signup) |
invalid_signup | 400 | Signup data failed validation |
verification_unavailable | 503 | Verification email could not be issued; signup failed closed |
auth_not_configured | 503 | App has no auth block or provisioning is incomplete |
auth_provider_unavailable | 503 | Identity provider is unreachable |
provider_not_enabled | 403 | The requested provider is not declared in auth {} |
missing_field | 400 | Required field not provided |
invalid_json | 400 | Request body is not valid JSON |
body_too_large | 413 | Request body exceeds 1 MB |
no_session | 401 | No session cookie present |
no_refresh_token | 401 | Session has no refresh token |
refresh_failed | 401 | Token refresh was rejected |
oauth_failed | 400 | OAuth provider returned an error |
invalid_state | 400 | OAuth state parameter mismatch (possible CSRF) |
missing_flow_state | 400 | OAuth flow cookie missing or expired |
linking_disabled | 403 | Account linking is disabled in the schema |
last_method | 400 | Cannot remove last authentication method |
session_mismatch | 403 | Session changed during OAuth linking flow |
Session Isolation
Each deployed app has its own session secret, generated during kizaki deploy and stored in Vault. Sessions are encrypted with AES-256-GCM using per-app keys.
- Sessions cannot be shared or reused across apps
- A session cookie from App A is meaningless to App B
- Compromising one app's secret does not affect others
How It Works
The auth module does not handle tokens directly. All auth state is managed via HttpOnly cookies:
login()callsPOST /__auth/login— the server validates credentials, sets a sealed session cookie, and returns the usergetAuthConfig()callsGET /__auth/config— the server returns the compiled provider/linking/session settings for the current apploginWithGoogle()navigates toGET /__auth/google— the server redirects through the OAuth flow, then sets a session cookie on callback- Subsequent API requests include the cookie automatically. The platform validates it and injects principal headers before forwarding to the runtime
logout()callsPOST /__auth/logout— the server clears the cookie
The identity provider is never visible to the end user.