Authentication
Set up user authentication with email/password, magic links, OAuth, API keys, and two-factor auth.
#Authentication
EmuView handles user identity and session management using Better Auth, running natively on Cloudflare Workers. You get multiple authentication methods out of the box — no backend code to write.
#What you can do
| Method | Description | Setup required |
|---|---|---|
| Email + password | Traditional sign-up and sign-in with PBKDF2 hashing | None (built-in) |
| Username + password | Sign in by username; email optional at signup | Enable in Settings → Authentication (guide) |
| Magic links | Passwordless login via email links | Cloudflare Email Routing |
| Google OAuth | Sign in with Google accounts | Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET env vars |
| GitHub OAuth | Sign in with GitHub accounts | Set GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET env vars |
| API keys | Server-side admin access with sk- prefixed tokens |
None (built-in) |
| Two-factor (TOTP) | Time-based one-time passwords via authenticator apps | None (built-in plugin) |
#Key concepts
| Concept | What it means |
|---|---|
| Session token | An opaque token from Better Auth, sent as Authorization: Bearer <token>. Managed automatically by the SDK |
| API key | A sk- prefixed key for server-side access. Stored as a SHA-256 hash in D1. Capabilities can be scoped per key |
| Role | One of 6 built-in roles assigned to each user: super_admin, admin, editor, viewer, api, public |
| Signup mode | Controls registration: open (anyone can register), invite (admin creates accounts), disabled (setup script only) |
#How it fits together
Authentication feeds into access control. When a request arrives:
- The auth middleware resolves the session or API key to a user identity
- The user's role determines their base permissions
- Per-collection access policies further restrict read/write/delete operations
- Row-level and column-level security filters the response
Here is the full journey from sign-in to an authorized API call:
sequenceDiagram
participant App as Your app
participant GW as Gateway Worker
participant BA as Better Auth
participant KV as Session cache (KV)
App->>GW: POST /api/auth/sign-in/email
GW->>BA: Verify credentials against D1
BA-->>App: Session token
App->>GW: GET /api/v1/... with Bearer token
GW->>KV: Look up hashed token
alt Cache miss
GW->>BA: Verify session
GW->>KV: Cache user (5 min TTL)
end
GW-->>App: Response filtered by role and access policiesThe gateway hashes the token before using it as a cache key, so raw tokens never appear in KV. Revoking a user or changing their role invalidates the cached entry immediately.
Never embed admin API keys in client-side code. Anyone can read them from browser developer tools. Use session tokens from auth.signIn() for frontend apps.
#Guides in this section
- Email and password — Set up traditional registration and login
- Username sign-in & optional email — Usernames as login identifiers, no-email accounts, shared-email recovery
- Magic links — Configure passwordless email authentication
- OAuth providers — Connect Google and GitHub sign-in
- API keys — Create and manage server-side access tokens
- Two-factor authentication — Add TOTP-based second factor