# AuthGate Outage — Postmortem Diagrams (2026-04-14)

## 1. System Overview

```mermaid
flowchart TB
    subgraph Client
        Browser["User Browser<br/><i>JWT in memory, session cookie</i>"]
    end

    subgraph Frontend
        NextJS["Next.js App<br/><i>Vercel</i><br/>/login · /auth/callback · /dashboard"]
    end

    subgraph API["API Gateway — ECS Fargate (×3 behind ALB)"]
        AuthRoutes["Auth Routes<br/>POST /auth/login<br/>GET /auth/callback<br/>POST /auth/refresh<br/>POST /auth/logout"]
        JWTMiddleware["JWT Verification Middleware"]
    end

    subgraph Internal["Internal Services — ECS Fargate (×2)"]
        Proxy["Token Exchange Proxy<br/><i>mTLS internal · TLS outbound</i><br/>⚠️ TLS cert expired 2026-04-14"]
    end

    subgraph Data["Data Stores"]
        PG["PostgreSQL (RDS)<br/><i>authgate_prod</i><br/>users · sessions · refresh_tokens"]
        Redis["Redis (ElastiCache)<br/><i>session cache · rate limiting</i><br/>TTL: 24h / 30d remember-me"]
    end

    subgraph External["External OAuth Providers"]
        Google["Google OAuth 2.0<br/><i>accounts.google.com</i>"]
        GitHub["GitHub OAuth<br/><i>github.com/login/oauth</i>"]
    end

    Browser -->|"HTTPS"| NextJS
    NextJS -->|"HTTPS"| AuthRoutes
    AuthRoutes --> JWTMiddleware
    AuthRoutes -->|"mTLS"| Proxy
    Proxy -->|"TLS"| Google
    Proxy -->|"TLS"| GitHub
    AuthRoutes --> PG
    AuthRoutes --> Redis

    style Proxy fill:#e74c3c,stroke:#c0392b,color:#fff
```

## 2. Refresh Token Rotation Flow

```mermaid
sequenceDiagram
    autonumber
    participant B as Browser
    participant F as Next.js Frontend
    participant A as API Gateway
    participant R as Redis
    participant P as PostgreSQL

    Note over B,F: Access token (JWT) has expired

    B->>F: Request to protected resource
    F->>A: Original request (expired JWT)
    A-->>F: 401 Unauthorized

    rect rgb(230, 245, 255)
        Note over F,P: Automatic refresh flow
        F->>A: POST /auth/refresh<br/>(session cookie)
        A->>R: Lookup session by cookie
        R-->>A: { user_id, refresh_token_hash }
        A->>P: SELECT refresh_token<br/>WHERE token_hash = $1
        P-->>A: refresh_token row

        Note over A: Validate: not expired, not revoked

        A->>A: Generate new JWT access token (15min TTL)
        A->>A: Generate new opaque refresh token (30d TTL)

        A->>P: INSERT ... ON CONFLICT (session_id)<br/>DO UPDATE token_hash, expires_at<br/>(revoke old token)
        Note over A,P: ⚠️ BUG: was plain INSERT →<br/>unique constraint violation on session_id
        P-->>A: OK

        A->>R: UPDATE session:{id}<br/>new refresh_token_hash
        R-->>A: OK

        A-->>F: 200 { access_token }
    end

    F->>A: Retry original request (new JWT)
    A-->>F: 200 OK (protected data)
    F-->>B: Render response
```

## 3. Data Model — users / sessions / refresh_tokens

```mermaid
erDiagram
    users {
        UUID id PK
        VARCHAR email UK "NOT NULL"
        VARCHAR name "NOT NULL"
        TEXT avatar_url
        VARCHAR oauth_provider "google | github"
        VARCHAR oauth_provider_id
        VARCHAR role "user | admin"
        TIMESTAMPTZ created_at
        TIMESTAMPTZ updated_at
    }

    sessions {
        UUID id PK
        UUID user_id FK "→ users.id CASCADE"
        VARCHAR session_token UK "NOT NULL"
        INET ip_address
        TEXT user_agent
        BOOLEAN remember_me "default false"
        TIMESTAMPTZ expires_at "NOT NULL"
        TIMESTAMPTZ created_at
        TIMESTAMPTZ last_active_at
    }

    refresh_tokens {
        UUID id PK
        UUID user_id FK "→ users.id CASCADE"
        UUID session_id FK_UK "→ sessions.id CASCADE · UNIQUE"
        VARCHAR token_hash UK "NOT NULL"
        TIMESTAMPTZ expires_at "NOT NULL"
        BOOLEAN revoked "default false"
        TIMESTAMPTZ revoked_at
        TIMESTAMPTZ created_at
    }

    users ||--o{ sessions : "has many"
    users ||--o{ refresh_tokens : "has many"
    sessions ||--o| refresh_tokens : "has one (unique session_id)"
```

---

*Generated for the 2026-04-14 SEV-1 login outage postmortem.*
