Quickback Docs

Auth

Sign users in for the components — Better Auth cookies in the browser, bearer tokens in React Native and other non-browser clients.

The components never touch credentials. The emitted client you pass to <QuickbackProvider client> decides how each request is authenticated.

ModeUse it forCredentialWrites need
auth: "cookie" (default)Browser appsBetter Auth session cookie, credentials: "include"The page's origin in the trusted origins
auth: "bearer"React Native, CLIs, serversAuthorization: Bearer <token>Nothing extra. Bearer requests skip the CSRF origin check.

Sign in with Better Auth's own client. The session cookie it sets is what the Quickback client sends.

import { createAuthClient } from "better-auth/react"
import { organizationClient } from "better-auth/client/plugins"
import { createClient } from "@/lib/quickback.client"

const baseURL = import.meta.env.VITE_API_URL // e.g. http://localhost:8787

export const auth = createAuthClient({
  baseURL,
  basePath: "/auth/v1",
  plugins: [organizationClient()],
})

export const client = createClient({ baseUrl: baseURL })

await auth.signIn.email({ email, password })
// Tenant-scoped tables are firewalled to the session's active organization.
await auth.organization.setActive({ organizationId })

The cookie only works if the API trusts your page's origin:

  • Same Worker (recommended). Mount the built app with apps.<name>. It is then served on the same origin as /api and /auth, and its hostname is trusted automatically.
  • Local dev. Until a project has a production domain, http://localhost:5173 (Vite), :3000, and :8787 are trusted by default.
  • Anywhere else. Add the origin to trustedOrigins. A write from an untrusted origin fails with 403 CSRF_ORIGIN_REJECTED, and the browser blocks reads through CORS.

React Native and other non-browser clients: bearer

React Native has no cookie jar and sends no Origin header. Cookie-backed writes therefore fail with 403 CSRF_ORIGIN_REJECTED. Use the bearer lane instead:

import * as SecureStore from "expo-secure-store"
import { createClient } from "@/lib/quickback.client"

const KEY = "quickback_session"

export const client = createClient({
  baseUrl: "https://api.example.com",
  auth: "bearer",
  sessionStore: {
    get: () => SecureStore.getItemAsync(KEY),
    set: (token) => (token ? SecureStore.setItemAsync(KEY, token) : SecureStore.deleteItemAsync(KEY)),
  },
})

// set-auth-token on /auth/v1/* is saved to sessionStore.
await client.request("POST", "/auth/v1/sign-in/email", { body: { email, password } })
await client.request("POST", "/auth/v1/organization/set-active", { body: { organizationId } })

Sign in through the client, as above. It saves the set-auth-token header: the signed <token>.<signature>. If you sign in with your own fetch, store that header too, never the body's token. The body carries the raw session id, and the generated API runs Better Auth's bearer({ requireSignature: true }). An unsigned token is silently ignored: get-session returns null and every API call answers 401.

With auth: "bearer", the client:

  • sends Authorization: Bearer <token> and defaults credentials to "omit";
  • caches the short-lived JWT that API responses return in set-auth-token (see JWT optimization);
  • on a 401 with a cached JWT, drops the JWT and retries once with the session token from sessionStore.

Full lane semantics: Bearer lane.

On this page