Skip to content

docs(design): multi-user auth + ownership design note (#113) - #167

Closed
lucastononro wants to merge 2 commits into
mainfrom
fix/113-multiuser-auth-design
Closed

docs(design): multi-user auth + ownership design note (#113)#167
lucastononro wants to merge 2 commits into
mainfrom
fix/113-multiuser-auth-design

Conversation

@lucastononro

@lucastononro lucastononro commented Jul 19, 2026

Copy link
Copy Markdown
Owner

Summary

Design note only — no code. Multi-user auth/ownership touches every model, all 15 routers, and the frontend, so this PR makes #113 build-ready instead of attempting a blind build: docs/design/multi-user-auth.md grounds the design in the current code (models.py, main.py, routers, services/usage.py, api.ts/AppContext) and in the two in-flight foundations — PR #138 (opt-in API_AUTH_TOKEN bearer middleware, #88) and PR #153 (Alembic, #121).

Proposed approach (short):

  • Data model: users (+ auth_sessions, api_tokens, project_shares) with owner_id FKs on Project/Experiment/RegisteredModel/Deployment; Project.owner_id is the authoritative access root, child owner_ids are creator attribution; usage_events.user_id for per-user cost.
  • Auth: AUTH_MODE = none | token | multi_user. Browser = HttpOnly session cookie (fixes SSE natively — EventSource sends cookies, so Opt-in bearer-token auth for /api/* (API_AUTH_TOKEN) #138's ?token= stays only for PAT/CLI clients); CLI = per-user PATs on the same Bearer wire shape as Opt-in bearer-token auth for /api/* (API_AUTH_TOKEN) #138; OIDC SSO later mints the same session. In none/token mode get_current_user resolves to a fixed default-owner user, so ownership code ships dark with zero behavior change.
  • Migration: three Alembic revisions on top of refactor(db): introduce Alembic migrations (replace boot-time _run_migrations) (#121) #153 — additive tables/nullable columns → backfill everything to a well-known default owner → NOT NULL flip; first real login "claims" the default owner's data.
  • Authorization/sharing: one rule (own / instance-visible / shared → read; own / can_write share → write; 404 for invisible objects), a reusable get_accessible_project dependency + scope_to_user query helper, per-router enforcement checklist, project-granularity share-by-link + instance visibility.
  • Rollout: 4 phases, each independently shippable, single-tenant open mode stays the default throughout.

Keeping #113 open — this is the design for #113, not the implementation; the issue should track the build phases. Same design posted as a comment on the issue.

Changes

  • docs/design/multi-user-auth.md (new) — the design note. No code, schema, or router changes; no docs index exists to update.

Test plan

n/a — documentation only; reviewer reads the design. Verified the branch contains a single new markdown file (no build impact).

Design for #113

🤖 Generated with Claude Code

Greptile Summary

This PR adds a documentation-only design note (docs/design/multi-user-auth.md) for multi-user authentication, ownership, and sharing (#113). No code, schema, or router changes are included; the document grounds the proposed design in the current codebase and the two in-flight foundation PRs (#138 bearer middleware, #153 Alembic).

  • Data model: introduces users, auth_sessions, api_tokens, project_shares tables plus owner_id FKs on Project/Experiment/RegisteredModel/Deployment and a nullable usage_events.user_id for per-user cost attribution.
  • Auth modes: none | token | multi_user ladder keeps today's single-tenant behaviour unchanged by default; browser uses HttpOnly session cookie (solves SSE natively), CLI/scripts use per-user PATs on the same Bearer wire shape as PR Opt-in bearer-token auth for /api/* (API_AUTH_TOKEN) #138.
  • Rollout: four independently shippable phases, riskiest DB step (NOT NULL flip) lands while the feature is dark.

Confidence Score: 4/5

Safe to merge as a documentation-only PR; one data model gap should be addressed before the Phase 1 implementation begins.

The document is thorough and all previous review threads are addressed inline. One genuine gap remains: usage_events.user_id is declared nullable-forever but the FK carries no ondelete directive, so deleting a user with attributed events would fail at the DB level — contradicting the 'unattributed rows' dashboard intent. This needs to be fixed in the design before the Alembic migration is written, otherwise the admin user-delete flow will be broken on the first real usage data.

Files Needing Attention: docs/design/multi-user-auth.md — specifically the UsageEvent.user_id FK definition (§3.5) and the ProjectShare.id PK style (§3.4).

Important Files Changed

Filename Overview
docs/design/multi-user-auth.md New design document for multi-user auth/ownership. Previous review threads are all addressed in this version. One data model gap: usage_events.user_id FK needs ondelete=SET NULL to allow user deletion without orphaning attributed events.

Sequence Diagram

sequenceDiagram
    participant B as Browser
    participant F as FastAPI
    participant DB as Database

    Note over B,DB: Phase 2 — multi_user mode login
    B->>F: "POST /api/auth/login {email, password}"
    F->>DB: "SELECT user WHERE email=..."
    DB-->>F: User row
    F->>F: argon2id verify password
    F->>DB: "INSERT auth_sessions (id=sha256(token), expires_at)"
    F-->>B: "200 Set-Cookie: trainable_session=raw_token HttpOnly SameSite=Lax"

    Note over B,DB: Subsequent requests (browser)
    B->>F: GET /api/projects (cookie auto-sent)
    F->>F: get_current_user: sha256(cookie) lookup
    F->>DB: SELECT auth_session + slide expiry
    DB-->>F: session + user
    F->>F: scope_to_user(stmt, user)
    F-->>B: 200 filtered project list

    Note over B,DB: AUTH_MODE=none (Phase 1, dark)
    B->>F: GET /api/projects (no credentials)
    F->>F: get_current_user returns default_owner (no DB lookup)
    F->>F: scope_to_user no-op
    F-->>B: 200 full project list (identical to today)
Loading

Reviews (2): Last reviewed commit: "fix(review): address Greptile findings o..." | Re-trigger Greptile

Comment thread docs/design/multi-user-auth.md
Comment thread docs/design/multi-user-auth.md
Comment thread docs/design/multi-user-auth.md
Comment thread docs/design/multi-user-auth.md
- ProjectShare: specify CHECK constraint + create-path validation for the
  user_id/link_token_hash mutual-exclusion invariant (P1)
- AuthSession: spell out the hash-the-cookie-once-at-lookup pattern
- Bootstrap: recommend env-var pre-seed; restrict interactive bootstrap
  to localhost / token-mode gate (first-come-first-served window)
- scope_to_user: state explicitly that pre-redemption link-browsing
  sessions are scoped to the single project only, by design
@lucastononro

Copy link
Copy Markdown
Owner Author

Merged into integration branch staging-v0.0.5 (see the STAGING-v0.0.5.md ledger on that branch for merge order, Greptile follow-ups and test results). These changes will land on main via the staging merge — closing to clear the queue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant