Auth System Architecture

Created Feb 25, 2026 public

Current state of the DocView authentication system — magic link auth, device code flow for CLI, and anonymous publish with account claiming.

Tech Stack

Database Schema

erDiagram
    user {
        text id PK
        text name
        text email UK
        bool emailVerified
        timestamp createdAt
    }
    session {
        text id PK
        text userId FK
        text token UK
        timestamp expiresAt
        text ipAddress
        text userAgent
    }
    account {
        text id PK
        text userId FK
        text providerId
        text accessToken
    }
    verification {
        text id PK
        text identifier
        text value
        timestamp expiresAt
    }
    device_codes {
        varchar code PK
        text session_token
        text user_id
        varchar status
        timestamp expires_at
    }
    documents {
        uuid id PK
        varchar slug UK
        text author_id FK
        varchar claim_token
        varchar visibility
        varchar private_key
    }

    user ||--o{ session : "has"
    user ||--o{ account : "has"
    user ||--o{ documents : "owns"
    device_codes }o--|| user : "approved by"

Magic Link Flow (Web)

The primary auth method. No passwords — email-only.

sequenceDiagram
    participant U as User
    participant B as Browser
    participant S as Server
    participant BA as Better Auth
    participant R as Resend API
    participant DB as PostgreSQL

    U->>B: Enter email
    B->>S: POST /api/auth/sign-in/magic-link
    S->>BA: Handle magic link request
    BA->>DB: Store verification token (15m TTL)
    BA->>R: Send email with magic link
    R-->>U: Email arrives

    U->>B: Click magic link
    B->>S: GET /api/auth/magic-link/verify?token=...
    S->>BA: Verify token
    BA->>DB: Delete verification token
    BA->>DB: Create session (30d TTL)
    BA-->>B: Set cookie: better-auth.session_token
    B-->>U: Logged in

Device Code Flow (CLI)

Allows CLI tools and MCP clients to authenticate without a browser session.

sequenceDiagram
    participant CLI as CLI / MCP
    participant S as Server
    participant DB as PostgreSQL
    participant B as Browser
    participant U as User

    CLI->>S: POST /api/device-code
    S->>DB: Insert code (8-char hex, 30m TTL)
    S-->>CLI: { code, verification_url }

    CLI->>U: "Open this URL and approve"
    U->>B: Open /auth/device?code=ABCD1234

    Note over B,S: User signs in via magic link if needed

    U->>B: Click "Approve"
    B->>S: POST /api/device-code/approve (with session cookie)
    S->>DB: UPDATE status='approved', session_token=cookie

    loop Poll every 3s
        CLI->>S: GET /api/device-code/:code
        S-->>CLI: { status, session_token }
    end

    CLI->>CLI: Save token to ~/.docview/token.json

Anonymous Publish + Account Claim

Users can publish without signing in, then claim ownership later.

sequenceDiagram
    participant U as Anonymous User
    participant S as Server
    participant DB as PostgreSQL

    U->>S: POST /api/docs (no auth)
    S->>DB: INSERT doc (author_id=NULL, claim_token=random 32 bytes)
    S-->>U: { url, claim_token }

    Note over U: User sees "Sign in to own this doc" banner

    U->>S: Magic link sign-in flow
    S-->>U: Authenticated, redirect with ?claim=token&auto_claim=1

    U->>S: POST /api/docs/:slug/claim { claim_token }
    S->>DB: UPDATE SET author_id=$userId, claim_token=NULL
    S-->>U: Document now owned

Session Resolution

How every request determines the authenticated user.

flowchart TD
    A[Request arrives] --> B{Cookie present?}
    B -->|Yes| C[Better Auth: getSession]
    C --> D{Valid session?}
    D -->|Yes| E[Set req.userId + req.userEmail]
    D -->|No| F{Authorization header?}
    B -->|No| F
    F -->|Bearer token| G[Query session table]
    G --> H{Token valid + not expired?}
    H -->|Yes| E
    H -->|No| I[Continue unauthenticated]
    F -->|None| I

    E --> J[Route handler]
    I --> J

    style E fill:#e8f5e9
    style I fill:#fff3e0

Route Auth Middleware

flowchart LR
    subgraph Public["No Auth Required"]
        A1[GET /d/:slug]
        A2[POST /api/device-code]
        A3[GET /api/device-code/:code]
    end

    subgraph Optional["optionalAuth"]
        B1[POST /api/docs — publish]
        B2[GET /api/docs/:slug/comments]
    end

    subgraph Required["requireAuth"]
        C1[GET /api/docs — list mine]
        C2[DELETE /api/docs/:slug]
        C3[POST /api/docs/:slug/comments]
        C4[POST /api/docs/:slug/claim]
        C5[POST /api/device-code/approve]
    end

    style Public fill:#e1f5fe
    style Optional fill:#fff9c4
    style Required fill:#fce4ec

Key Files

File Purpose
src/auth.js Better Auth config, magic link plugin
src/server.js Session middleware, Bearer token fallback
src/routes/api.js Auth middleware, all API endpoints
src/routes/viewer.js Device approval page, claim token in viewer
client/scripts/doc.js Web UI sign-in, claim banner, auto-claim
mcp/index.js CLI tools: publish, login, list, delete
mcp/auth.js Session file persistence (~/.docview/token.json)

Security Notes

No passwords are stored. Authentication is email-based only via magic links.