Skip to content

Authentication overview

Every project has its own user directory. Your app’s users sign up and sign in against your project (the routes live under /projects/{projectId}/auth/...), and each successful sign-in returns an access token that identifies the user to the data API, storage, functions and realtime.

This page is about your app’s users. Signing in to the VAIF Studio console itself, including with your company’s identity provider, is covered in Single sign-on.

Method Status
Email and password Available
Magic links (passwordless email) Available
Two-factor sign-in with an authenticator app (TOTP) and backup codes Available
Password reset and email verification Available
Device sessions (list and revoke) Available
Social sign-in Available. See Social sign-in

Create the client with your project ID and publishable key:

import { createVaifClient } from "@vaifstudio/client";
let accessToken = "";
const vaif = createVaifClient({
baseUrl: "https://api.vaif.studio",
projectId: process.env.VAIF_PROJECT_ID,
apiKey: process.env.VAIF_PUBLISHABLE_KEY,
accessToken: () => accessToken,
});
await vaif.auth.signUp("sam@example.com", "a-long-unique-passphrase");
const result = await vaif.auth.login("sam@example.com", "a-long-unique-passphrase");
if ("challengeToken" in result) {
// The user has two-factor sign-in on: ask for the 6-digit code, then finish signing in.
const session = await vaif.auth.mfa.verify(result.challengeToken, "123456");
accessToken = session.accessToken;
} else if ("accessToken" in result) {
accessToken = result.accessToken;
}
const me = await vaif.auth.getUser();
console.log(me.email);
await vaif.auth.logout();

The SDK does not store the token. Keep it (in memory, or wherever your app keeps secrets) and hand it back through accessToken, as above, so every request carries it.

await vaif.auth.magic.send("sam@example.com", {
redirectUrl: "https://app.example.com/auth/callback",
});

The email’s link signs the user in and redirects to redirectUrl with the token in the URL fragment: #access_token=…, or #mfa_challenge=… when the user has two-factor sign-in on. Read it on your callback page:

const params = new URLSearchParams(window.location.hash.slice(1));
const token = params.get("access_token");
const challenge = params.get("mfa_challenge");
if (challenge) {
const session = await vaif.auth.mfa.verify(challenge, "123456");
console.log(session.accessToken);
} else if (token) {
console.log("signed in", token.length > 0);
}

The redirect URL’s origin must be on your project’s allowed redirect URLs (Authentication > URL configuration in the console).

Two-factor sign-in is allowed when Allow two-factor sign-in is on under Authentication > Multi-factor. Users then turn it on for themselves in two steps:

// 1. Start: show a QR code for otpauthUri (or the secret for manual entry).
const { otpauthUri, secret, enrollToken } = await vaif.auth.mfa.enrollStart();
console.log(otpauthUri, secret);
// 2. Confirm with the first code from their app. Show the backup codes once.
const { backupCodes } = await vaif.auth.mfa.enrollConfirm(enrollToken, "123456");
console.log(backupCodes);

After that, login() answers { mfaRequired: true, challengeToken } and the user finishes with vaif.auth.mfa.verify(challengeToken, code). A backup code works in place of a code. vaif.auth.mfa.disable(code) turns two-factor sign-in off, and vaif.auth.mfa.regenerateCodes(code) issues new backup codes.

await vaif.auth.requestPasswordReset({ email: "sam@example.com", redirectUrl: "https://app.example.com/reset" });
// On the reset page, with the token from the link:
await vaif.auth.confirmPasswordReset({ token: "token-from-the-link", password: "a-new-passphrase" });
await vaif.auth.requestEmailVerification({ redirectUrl: "https://app.example.com/verified" });
await vaif.auth.confirmEmailVerification({ token: "token-from-the-link" });

User table sync is on by default: every project copies each of its users into a users table in the project (id, email, phone, provider, metadata, last_sign_in_at, created_at, updated_at), so you can query and join users with the data API. The table is created at the first sign-up, with a row rule under which each signed-in user sees only their own row. A user’s row is written when they sign up and updated each time they sign in.

To copy users who existed before the table did, call POST /projects/{projectId}/auth/sync-users once as a project admin. To sync into a table with another name, use Sync users to a table on the Authentication users page.

To turn sync off, set userTableName to null with PATCH /projects/{projectId}/auth/settings, as a project admin with a console access token:

Terminal window
curl -X PATCH https://api.vaif.studio/projects/$PROJECT_ID/auth/settings \
-H "Authorization: Bearer $VAIF_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "userTableName": null }'

Users then stop being copied. A table that already exists, and its rows, stay until you remove them.