Skip to content

Authentication

Login, register, OAuth2, OTP / magic-link, MFA, anonymous sessions, refresh, logout.

The SDK exposes auth flows under three namespaces:

  • vb.auth.<collection> — per auth-collection user flows (login, register, OAuth, OTP, MFA, anonymous, totp)
  • vb.auth.admin — admin-only flows (/api/v1/admin/auth/*)
  • vb.auth.shared — flows that don’t depend on the collection (refresh, logout, /me)

The <collection> is whatever you named your auth collection (commonly users).

// Register
await vb.auth.users.register({
email: "alice@example.com",
password: "long-password-here",
// any extra fields defined on the collection
name: "Alice",
});
// Login → token persisted in the auth store
const result = await vb.auth.users.login({
email: "alice@example.com",
password: "long-password-here",
});
if ("mfa_required" in result) {
// MFA enabled — prompt for the 6-digit code
await vb.auth.users.loginMfa({
mfa_token: result.mfa_token,
code: "123456",
});
} else {
console.log("Logged in as", result.record);
}
await vb.auth.users.requestVerify(); // sends the email
await vb.auth.users.verifyEmail("token-from-link");
await vb.auth.users.requestPasswordReset("alice@example.com");
await vb.auth.users.confirmPasswordReset("reset-token", "new-password");
await vb.auth.users.otpRequest("alice@example.com");
// User pastes the 6-digit code (or clicks the magic link)
const result = await vb.auth.users.otpAuth({
email: "alice@example.com",
code: "123456",
});

Pairs with MFA: if the user has TOTP enabled, the result is MfaPending with a fresh mfa_token to complete via loginMfa.

// 1. Begin enrolment
const setup = await vb.auth.users.totpSetup();
// setup.secret → base32 secret (for manual entry)
// setup.otpauth_url → otpauth://… URI, render it as a QR code
// 2. User scans + enters a code from their authenticator
const { recovery_codes } = await vb.auth.users.totpConfirm("123456");
// → { enabled: true, recovery_codes: ["XXXX-XXXX", …] } (first 10 codes, shown once)
// recovery_codes is in the SDK's return type from 0.7.3; older SDKs still return it at runtime
// 3. Recovery codes — needs a current authenticator code (server 0.17+)
const codes = await vb.auth.users.recoveryRegenerate("123456");
// → { codes: ["XXXX-XXXX", …, …] } (10 fresh codes)
const status = await vb.auth.users.recoveryStatus();
// → { total: 10, remaining: 10 }
// 4. Disable
await vb.auth.users.totpDisable("123456");

During login with MFA enabled:

const result = await vb.auth.users.login({ email, password });
if ("mfa_required" in result) {
// either a TOTP code or a recovery code:
await vb.auth.users.loginMfa({ mfa_token: result.mfa_token, code: "123456" });
// or:
await vb.auth.users.loginMfa({ mfa_token: result.mfa_token, recovery_code: "XXXX-XXXX" });
}

Vaultbase ships out-of-the-box providers — Google, GitHub, GitLab, Facebook, Microsoft, Discord, Twitch, Spotify, LinkedIn, Slack, Bitbucket, Notion, Patreon, Apple, Twitter / X, plus generic OIDC.

// 1. Discover what's enabled on your server
const providers = await vb.auth.users.oauth2Providers();
// → [{ name: "google", displayName: "Google" }, …]
// 2. Start the flow and redirect the browser
const { authorize_url } = await vb.auth.users.oauth2Authorize({
provider: "google",
redirectUri: "https://app.example.com/oauth/callback",
});
window.location.assign(authorize_url);
// 3. On the callback page (same browser tab)
const params = new URLSearchParams(location.search);
const result = await vb.auth.users.oauth2Exchange({
provider: "google",
code: params.get("code")!,
state: params.get("state")!,
redirectUri: "https://app.example.com/oauth/callback",
});
// → LoginResult (token persisted automatically), MfaPending, or a merge prompt
if ("mfa_required" in result) {
await vb.auth.users.loginMfa({ mfa_token: result.mfa_token, code: "123456" });
}

oauth2Authorize() returns a state and a state_secret; server 0.17+ requires both back at the exchange, which proves the callback is finishing the flow this browser started. The SDK keeps the secret in sessionStorage and sends it for you. If the callback runs somewhere else (another tab, a server, a native app), save state_secret yourself and pass it to oauth2Exchange(). PKCE is off unless you ask for it: pass use_pkce: "1" to oauth2Authorize() for server-managed PKCE, or your own code_challenge (then code_verifier to the exchange). Twitter / X always uses PKCE.

If the OAuth identity matches an existing email but isn’t yet linked, the exchange returns a merge_token instead. Confirm with the account’s password (with MFA on, this returns an mfa_token; the link is applied once loginMfa() succeeds):

await vb.auth.users.oauth2MergeConfirm({ merge_token, password: "current-pw" });

Unlink:

await vb.auth.users.oauth2Unlink("google");
const guest = await vb.auth.users.anonymous();
// guest.token persisted; backed by an `is_anonymous: 1` user row
// Promote to a real account when ready (preserves user.id)
await vb.auth.users.promote({
email: "alice@example.com",
password: "long-password-here",
});

The SDK refreshes a token on its own when it’s within a minute of expiry, and once after a 401. A 401 on a request sent without the session (for example a wrong password at login) keeps the existing session; the error’s reason is "invalid". Sessions are per client: each Vaultbase instance refreshes its own token (a LocalStorageAuthStore still shares the token across tabs). A session can be refreshed up to the server’s maximum age (auth.session.max_seconds, 30 days by default); after that the user signs in again.

// Bumps the token's expiry against the same principal.
await vb.auth.shared.refresh();
// Server-side jti revocation + client-side store clear.
await vb.auth.shared.logout();
await vb.auth.admin.login({ email, password });
const me = await vb.auth.admin.me(); // throws a 401 VaultbaseError if not logged in
await vb.auth.shared.logout(); // admin and user sessions sign out the same way

The SDK persists the issued token in your configured AuthStore:

vb.client.authStore.get(); // → { token, record } | null
vb.client.authStore.set({ token: "manual-token", record: { id: "u1" } });
vb.client.authStore.set(null); // sign out without a server round-trip

Every authed request reads from the store, attaches Authorization: Bearer <token> (or rides the cookie via withCredentials), and falls back gracefully to skipAuth: true for endpoints that don’t need it (e.g. /login itself).