DocView Authentication Process
DocView uses Better Auth with magic link email authentication, plus a device-code flow for CLI clients.
Overview
flowchart TD
Client[Client Request] --> SessionMW[Session Middleware]
SessionMW -->|Cookie present| BetterAuth[Better Auth getSession]
SessionMW -->|Bearer token| DBLookup[Direct DB session lookup]
SessionMW -->|Neither| Unauthed[Continue unauthenticated]
BetterAuth -->|Valid| Authed[req.userId + req.userEmail set]
DBLookup -->|Valid token| Authed
Authed --> Routes[Route handlers]
Unauthed --> Routes1. Magic Link Flow (Browser)
The primary auth method — passwordless email login via Better Auth's magic link plugin.
sequenceDiagram
participant U as User
participant B as Browser
participant S as DocView Server
participant BA as Better Auth
participant E as Resend (Email)
U->>B: Enters email on login page
B->>S: POST /api/auth/magic-link
S->>BA: magicLink plugin
BA->>E: Send email with signed URL
E->>U: Email with magic link
U->>B: Clicks magic link
B->>S: GET /api/auth/magic-link/verify?token=...
S->>BA: Verify token, create session
BA->>B: Set session cookie
Note over B: better-auth.session_token cookie setKey details:
- Email/password login is disabled — magic links only
- If
RESEND_API_KEYis not set, the magic link is logged to console (dev mode) - Better Auth handles all
/api/auth/*routes viatoNodeHandler(auth)
2. Device Code Flow (CLI)
An OAuth2-style device authorization flow that lets CLI tools authenticate by having the user approve a code in their browser.
sequenceDiagram
participant CLI as CLI Tool
participant S as DocView Server
participant B as User's Browser
CLI->>S: POST /api/device-code
S-->>CLI: { code: "a1b2c3d4", verification_url, expires_in: 1800 }
Note over CLI: Display code + URL to user
loop Poll every few seconds
CLI->>S: GET /api/device-code/a1b2c3d4
S-->>CLI: { status: "pending" }
end
Note over B: User opens verification_url
B->>S: POST /api/device-code/approve (with session cookie + code)
Note over S: Extracts raw session cookie value<br/>Stores in device_codes table
CLI->>S: GET /api/device-code/a1b2c3d4
S-->>CLI: { status: "approved", session_token: "better-auth.session_token=..." }
Note over CLI: Store token for future Bearer auth
Note over S: Device code row deleted after retrievalKey details:
- Device codes are 8 hex characters, expire after 30 minutes
- On approval, the server extracts the approving user's raw session cookie and stores it
- The CLI receives the full
better-auth.session_token=...cookie value - Device code rows are deleted after the CLI polls the approved result (one-time use)
3. Session Resolution Middleware
Every request passes through a global middleware (server.js:16-55) that resolves the user identity:
flowchart TD
Req[Incoming Request] --> TryCookie{Has session cookie?}
TryCookie -->|Yes| GetSession[auth.api.getSession<br/>via Better Auth]
GetSession -->|Valid| SetUser[Set req.userId + req.userEmail]
GetSession -->|Invalid| CheckBearer
TryCookie -->|No| CheckBearer{Has Authorization:<br/>Bearer header?}
CheckBearer -->|Yes| DBQuery[Query session table<br/>WHERE token = ? AND not expired]
DBQuery -->|Found| SetUser
DBQuery -->|Not found| NoAuth[Continue unauthenticated]
CheckBearer -->|No| NoAuth
SetUser --> Next[next]
NoAuth --> NextTwo auth paths, unified result:
| Method | Source | Used By |
|---|---|---|
| Cookie | better-auth.session_token cookie |
Browser sessions |
| Bearer | Authorization: Bearer <token> |
CLI (from device flow) |
4. Route-Level Authorization
Routes use two middleware helpers from routes/api.js:
optionalAuth— proceeds whether or not the user is authenticated (for public doc viewing)requireAuth— returns401ifreq.userIdis not set (for mutations: create, update, delete, comment)
5. Document Visibility
Documents have a visibility field (public or private):
- Public docs are readable by anyone
- Private docs require either:
- The
?key=<private_key>query parameter, OR - Being the document author (checked via
req.userId)
- The