Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/trailbaseio/trailbase/llms.txt

Use this file to discover all available pages before exploring further.

TrailBase provides built-in authentication with support for password-based login, OAuth providers, email verification, and password reset flows.

Overview

TrailBase authentication features:
  • Password authentication - Email and password login with configurable policies
  • OAuth providers - Google, GitHub, Discord, Microsoft, and more
  • Email verification - Confirm user email addresses
  • Password reset - Secure password recovery via email
  • Session management - JWT tokens with refresh tokens
  • Admin accounts - Special privileges for administrative users

User Table

TrailBase includes a built-in _user table:
CREATE TABLE _user (
  id            BLOB PRIMARY KEY DEFAULT (uuid_v7()),
  email         TEXT NOT NULL UNIQUE,
  password_hash TEXT,
  verified      INTEGER DEFAULT 0,
  admin         INTEGER DEFAULT 0,
  created       INTEGER DEFAULT (UNIXEPOCH()),
  updated       INTEGER DEFAULT (UNIXEPOCH())
);
Do not modify the _user table directly. Use TrailBase’s auth APIs and CLI commands instead.

Password Authentication

Enable Password Auth

Password authentication is enabled by default. Configure it in traildepot/config.textproto:
traildepot/config.textproto
auth {
  # Disable password auth (optional)
  disable_password_auth: false
  
  # Password policy
  password_policy {
    min_length: 8
    require_uppercase: true
    require_lowercase: true
    require_number: true
    require_special: false
  }
  
  # Email verification settings
  require_email_verification: true
}

server {
  site_url: "http://localhost:4000"
}

email {
  # Configure SMTP for email verification
  smtp_host: "smtp.gmail.com"
  smtp_port: 587
  smtp_username: "your-email@gmail.com"
  smtp_password: "your-app-password"
  from_address: "noreply@yourdomain.com"
}

User Registration

import { initClient } from "trailbase";

const client = initClient("http://localhost:4000");

// Register new user
try {
  await client.register({
    email: "user@example.com",
    password: "SecurePass123",
    password_repeat: "SecurePass123",
  });
  
  console.log("Registration successful! Check email for verification.");
} catch (error) {
  console.error("Registration failed:", error);
}
If email verification is enabled, users must click the verification link in their email before logging in.

User Login

import { initClient } from "trailbase";

const client = initClient("http://localhost:4000");

// Login
try {
  const response = await client.login({
    email: "user@example.com",
    password: "SecurePass123",
  });
  
  // Tokens are automatically stored by the client
  console.log("Logged in:", client.user());
  // { id: "...", email: "user@example.com", admin: false }
} catch (error) {
  console.error("Login failed:", error);
}

Token Management

The client automatically handles token storage and refresh:
import { initClient } from "trailbase";

const client = initClient("http://localhost:4000");

// Check if user is logged in
if (client.user()) {
  console.log("User:", client.user());
}

// Get tokens
const tokens = client.tokens();
if (tokens) {
  console.log("Auth token:", tokens.auth_token);
  console.log("Refresh token:", tokens.refresh_token);
}

// Logout
await client.logout();

Session Persistence

Tokens are stored in localStorage (browser) or secure storage (mobile):
// Client automatically restores session on initialization
const client = initClient("http://localhost:4000");

// User is automatically logged in if valid tokens exist
if (client.user()) {
  console.log("Restored session for:", client.user()?.email);
}

OAuth Providers

TrailBase supports multiple OAuth providers:
  • Google
  • GitHub
  • Discord
  • Microsoft
  • GitLab
  • And more…

Configure OAuth Provider

1

Register OAuth Application

Create an OAuth app with your provider:GitHub:
  1. Go to Settings → Developer settings → OAuth Apps
  2. Click “New OAuth App”
  3. Set callback URL: http://localhost:4000/_/auth/oauth/callback/github
  4. Save Client ID and Client Secret
Google:
  1. Go to Google Cloud Console
  2. Create a new project
  3. Enable Google+ API
  4. Create OAuth 2.0 credentials
  5. Add authorized redirect URI: http://localhost:4000/_/auth/oauth/callback/google
2

Add Provider to Config

Edit traildepot/config.textproto:
traildepot/config.textproto
auth {
  oauth_providers: [
    {
      key: "github"
      value {
        client_id: "your-github-client-id"
        client_secret: "your-github-client-secret"
        provider_id: GITHUB
      }
    },
    {
      key: "google"
      value {
        client_id: "your-google-client-id.apps.googleusercontent.com"
        client_secret: "your-google-client-secret"
        provider_id: GOOGLE
      }
    }
  ]
}

server {
  # Required for OAuth redirects
  site_url: "http://localhost:4000"
}
3

Restart TrailBase

trail run

OAuth Login Flow

import { initClient } from "trailbase";

const client = initClient("http://localhost:4000");

// Redirect to OAuth provider
function loginWithGitHub() {
  window.location.href = client.oauthUrl("github", {
    redirect_uri: window.location.origin + "/callback",
  });
}

// Handle callback on return
const params = new URLSearchParams(window.location.search);
if (params.has("code")) {
  // TrailBase automatically handles the OAuth callback
  // and sets the auth tokens
  console.log("Logged in:", client.user());
}
Mobile Apps: Use PKCE (Proof Key for Code Exchange) for secure OAuth flows without exposing client secrets.

Custom URI Schemes (Mobile)

For mobile apps, configure custom URI schemes:
traildepot/config.textproto
auth {
  # Allow deep links for mobile apps
  custom_uri_schemes: ["myapp"]
  
  oauth_providers: [
    {
      key: "github"
      value {
        client_id: "your-client-id"
        client_secret: "your-client-secret"
        provider_id: GITHUB
      }
    }
  ]
}
OAuth redirects will now work with myapp://auth/callback.

Email Verification

Send Verification Email

After registration, TrailBase automatically sends a verification email if configured:
traildepot/config.textproto
auth {
  require_email_verification: true
}

email {
  smtp_host: "smtp.gmail.com"
  smtp_port: 587
  smtp_username: "your-email@gmail.com"
  smtp_password: "your-app-password"
  from_address: "noreply@yourdomain.com"
}

Verify Email Manually

Users can request a new verification email:
await client.requestVerificationEmail();

CLI Verification

For development, verify users via CLI:
# Verify a user
trail user verify user@example.com true

# List unverified users
sqlite3 traildepot/data/main.db "SELECT email FROM _user WHERE verified = 0;"

Password Reset

Request Password Reset

import { initClient } from "trailbase";

const client = initClient("http://localhost:4000");

// Send password reset email
await client.requestPasswordReset("user@example.com");

Reset Password with Token

The reset email contains a link with a token. Users submit a new password:
// Extract token from URL: ?token=abc123
const params = new URLSearchParams(window.location.search);
const token = params.get("token");

if (token) {
  await client.resetPassword({
    token,
    password: "NewSecurePass123",
    password_repeat: "NewSecurePass123",
  });
}

User Management CLI

Manage users via the CLI:

Create Admin User

# Add a new verified admin user
trail user add admin@example.com SecurePass123
trail admin promote admin@example.com

List Admin Users

trail admin list

Promote/Demote Users

# Promote user to admin
trail admin promote user@example.com

# Demote admin to regular user
trail admin demote admin@example.com

Change User Password

trail user change-password user@example.com NewPassword123

Change User Email

trail user change-email old@example.com new@example.com

Delete User

trail user delete user@example.com

Invalidate Sessions

Force a user to re-login:
trail user invalidate-session user@example.com

Mint Auth Token

Generate an auth token for a user (useful for automation):
trail user mint-token user@example.com

Access Control

Protect your Record APIs with authentication:
traildepot/config.textproto
record_apis: [
  {
    name: "todos"
    table_name: "todos"
    # Only authenticated users can access
    acl_authenticated: [CREATE, READ, UPDATE, DELETE]
  },
  {
    name: "admin_settings"
    table_name: "settings"
    # Only admins can access
    acl_admin: [CREATE, READ, UPDATE, DELETE]
  }
]

Row-Level Access Control

Restrict access to specific rows:
traildepot/config.textproto
record_apis: [
  {
    name: "todos"
    table_name: "todos"
    autofill_missing_user_id_columns: true
    acl_authenticated: [CREATE, READ, UPDATE, DELETE]
    
    # Users can only see their own todos
    read_access_rule: "_ROW_.user = _USER_.id"
    
    # Users can only update their own todos
    update_access_rule: "_ROW_.user = _USER_.id"
    
    # Users can only delete their own todos
    delete_access_rule: "_ROW_.user = _USER_.id"
  }
]

Complex Access Rules

Use SQL expressions for advanced access control:
traildepot/config.textproto
record_apis: [
  {
    name: "articles"
    table_name: "articles"
    acl_authenticated: [CREATE, READ, UPDATE, DELETE]
    
    # Only editors can create articles
    create_access_rule: "EXISTS(SELECT * FROM editors WHERE user = _USER_.id)"
    
    # Authors can update their own articles
    update_access_rule: "_ROW_.author = _USER_.id AND EXISTS(SELECT * FROM editors WHERE user = _USER_.id)"
    
    # Authors can delete their own articles
    delete_access_rule: "_ROW_.author = _USER_.id"
  }
]
Access Rule Variables:
  • _USER_.id - Current user’s ID
  • _USER_.email - Current user’s email
  • _USER_.admin - Whether user is admin
  • _ROW_.* - Column values from the row being accessed
  • _REQ_.* - Values from the request body (for CREATE/UPDATE)

User Profiles

Extend user data with a profiles table:
migrations/main/U1234567890__create_profiles.sql
CREATE TABLE profiles (
    user         BLOB PRIMARY KEY NOT NULL REFERENCES _user(id) ON DELETE CASCADE,
    username     TEXT NOT NULL CHECK(username REGEXP '^[\w]{3,}$'),
    bio          TEXT,
    avatar_url   TEXT,
    created      INTEGER DEFAULT (UNIXEPOCH()) NOT NULL,
    updated      INTEGER DEFAULT (UNIXEPOCH()) NOT NULL
) STRICT;

CREATE UNIQUE INDEX _profiles__username_index ON profiles (username);

CREATE TRIGGER _profiles__updated_trigger AFTER UPDATE ON profiles FOR EACH ROW
  BEGIN
    UPDATE profiles SET updated = UNIXEPOCH() WHERE user = OLD.user;
  END;
Expose via API:
traildepot/config.textproto
record_apis: [
  {
    name: "profiles"
    table_name: "profiles"
    acl_authenticated: [CREATE, READ]
    acl_world: [READ]
    # Users can only create their own profile
    create_access_rule: "_REQ_.user = _USER_.id"
  }
]

Avatar Uploads

TrailBase provides built-in avatar support:
import { initClient } from "trailbase";

const client = initClient("http://localhost:4000");

// Upload avatar
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];

await client.uploadAvatar(file);

// Get avatar URL
const avatarUrl = client.avatarUrl();
if (avatarUrl) {
  console.log("Avatar:", avatarUrl);
  // http://localhost:4000/api/auth/avatar/user-id
}

Next Steps

First App

Add auth to your first app

Database Setup

Link users with your tables

File Uploads

Handle user file uploads

CLI Usage

Manage users via CLI