Being Human — Internal Docs
API

Auth & Accounts

better-auth, sessions, and how to call it from the web app

Auth is handled entirely by better-auth, mounted at /api/auth/*. We don't hand-roll any of it — sign-up, sign-in, sign-out, session management, and (later) OAuth all come from its handler. Its interactive reference lives at /api/auth/reference.

The flows

# Sign up (also signs you in — returns a session cookie)
curl -X POST http://localhost:3001/api/auth/sign-up/email \
  -H 'Content-Type: application/json' \
  -d '{"name": "Doug", "email": "doug@example.com", "password": "…"}'

# Sign in
curl -X POST http://localhost:3001/api/auth/sign-in/email \
  -H 'Content-Type: application/json' \
  -d '{"email": "doug@example.com", "password": "…"}'

# Who am I? (send the cookie)
curl http://localhost:3001/api/me -b cookies.txt
# → {"user": {...}} or {"user": null}

From the web app

Use the better-auth client, not hand-rolled fetches:

import { createAuthClient } from "better-auth/react";

export const authClient = createAuthClient({
  baseURL: "http://localhost:3001", // the API
});

// authClient.signUp.email({...}), authClient.signIn.email({...}),
// authClient.useSession(), authClient.signOut()

CORS is already configured for credentialed requests from CORS_ORIGINS.

Protecting API routes

requireAuth(auth) middleware (src/require-auth.ts) 401s without a valid session and puts the user on the context:

app.use("/unlock", requireAuth(auth));
// later, in the handler:
const user = c.get("user");

Adding OAuth providers

Discord/Google are config-only in src/auth.ts (socialProviders). Steam is special: it's OpenID 2.0, not OAuth2 — when we add "Sign in with Steam" it goes through better-auth's generic provider plugin, and the resulting account row stores the player's steamId64 as accountId with providerId: "steam". Achievement mirroring already reads exactly that (see Achievements).

Production notes

  • BETTER_AUTH_SECRET is required in production (env validation refuses to boot without it); dev uses a hardcoded fallback.
  • Sessions are cookie-based; the game client will authenticate the same way (or we add bearer-token support via a better-auth plugin when the Rust client needs it).

On this page