Quickback Docs

Auth Plugins

Quickback ships with a curated set of Better Auth plugins and helper wiring so your auth stack is production-ready by default. Enable these in quickback.config.ts under providers.auth.

Available Plugins

Email OTP (with AWS SES)

Quickback wires the Better Auth Email OTP flow to AWS SES. When emailOtp is enabled, the compiler emits the SES plugin and combo-auth email flow (magic link + OTP).

Enable in config:

import { defineAuth, defineConfig, defineDatabase, defineRuntime, defineStorage } from "@quickback/compiler";

export default defineConfig({
  name: "quickback-api",
  providers: {
    runtime: defineRuntime("cloudflare"),
    database: defineDatabase("cloudflare-d1", {
      vars: {
        AWS_SES_REGION: "us-east-2",
        EMAIL_FROM: "noreply@yourdomain.com",
        EMAIL_FROM_NAME: "Your App | Account Services",
        APP_NAME: "Your App",
        APP_URL: "https://account.yourdomain.com",
        BETTER_AUTH_URL: "https://api.yourdomain.com",
      },
    }),
    auth: defineAuth("better-auth", {
      emailAndPassword: { enabled: true },
      plugins: ["emailOtp"],
    }),
  },
});

Endpoints:

  • POST /auth/v1/email-otp/send-verification-otp
  • POST /auth/v1/email-otp/verify-otp

Email readiness check:

Use this to show UI warnings when SES isn't configured.

  • GET /api/v1/system/email-status
  • Response: { "emailConfigured": true|false }

Upgrade Anonymous

Adds a first-class endpoint to convert an anonymous user into a full user. This flips isAnonymous to false and refreshes the session cache immediately.

Enable in config:

auth: defineAuth("better-auth", {
  emailAndPassword: { enabled: true },
  plugins: ["anonymous", "upgradeAnonymous"],
}),

Endpoint:

  • POST /auth/v1/upgrade-anonymous

Note: If you send Content-Type: application/json, include a body (an empty {} is fine). This avoids request parsing errors in some clients.

AWS SES Plugin

This is a Quickback-provided Better Auth plugin used by Email OTP. It handles SES signing and delivery and is auto-included when emailOtp is enabled.

Required vars (use Wrangler secrets for credentials):

  • AWS_ACCESS_KEY_ID (secret)
  • AWS_SECRET_ACCESS_KEY (secret)
  • AWS_SES_REGION
  • EMAIL_FROM

Optional vars:

  • EMAIL_FROM_NAME
  • EMAIL_REPLY_TO - Reply-to address for emails (defaults to EMAIL_FROM)
  • APP_NAME
  • APP_URL
  • BETTER_AUTH_URL

Magic links provide passwordless authentication by sending a unique login link to the user's email. When clicked, the link authenticates the user without requiring a password.

Enable in config:

auth: defineAuth("better-auth", {
  plugins: ["magicLink"],
}),

Endpoints:

  • POST /auth/v1/magic-link/send - Send magic link email
  • GET /auth/v1/magic-link/verify - Verify magic link token

Email customization:

Magic link emails use the same AWS SES configuration as Email OTP. Customize the email template via environment variables:

  • APP_NAME - Application name in email subject
  • APP_URL - Base URL for magic link redirect
  • EMAIL_FROM - Sender email address
  • EMAIL_FROM_NAME - Sender display name

Frontend integration:

// Request magic link
await fetch('/auth/v1/magic-link/send', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: 'user@yourdomain.com' })
});

// User clicks link in email, which redirects to your app
// The token is verified automatically via the callback URL

Passkeys

Passkeys provide passwordless authentication using WebAuthn/FIDO2. Users authenticate with biometrics (fingerprint, face) or device PIN instead of passwords.

Enable in config:

auth: defineAuth("better-auth", {
  plugins: ["passkey"],
}),

Required environment variable:

  • ACCOUNT_URL - Your account/frontend URL (used as the relying party origin for WebAuthn)

Endpoints:

  • POST /auth/v1/passkey/register/options - Get registration challenge
  • POST /auth/v1/passkey/register/verify - Complete registration
  • POST /auth/v1/passkey/authenticate/options - Get authentication challenge
  • POST /auth/v1/passkey/authenticate/verify - Complete authentication
  • GET /auth/v1/passkey/list-user-passkeys - List user's registered passkeys
  • POST /auth/v1/passkey/delete-passkey - Delete a registered passkey

Browser support:

Passkeys are supported in all modern browsers:

  • Chrome 67+
  • Safari 14+
  • Firefox 60+
  • Edge 79+

Registration flow:

// 1. Get registration options
const optionsRes = await fetch('/auth/v1/passkey/register/options', {
  method: 'POST',
  credentials: 'include'
});
const options = await optionsRes.json();

// 2. Create credential using WebAuthn API
const credential = await navigator.credentials.create({
  publicKey: options
});

// 3. Send credential to server
await fetch('/auth/v1/passkey/register/verify', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  credentials: 'include',
  body: JSON.stringify(credential)
});

Authentication flow:

// 1. Get authentication options
const optionsRes = await fetch('/auth/v1/passkey/authenticate/options', {
  method: 'POST'
});
const options = await optionsRes.json();

// 2. Get credential using WebAuthn API
const credential = await navigator.credentials.get({
  publicKey: options
});

// 3. Verify credential
await fetch('/auth/v1/passkey/authenticate/verify', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  credentials: 'include',
  body: JSON.stringify(credential)
});

Where This Runs

All Better Auth plugin routes are served under your auth base path:

/auth/v1/*

On this page