Skip to content

Keycloak-backed auth + tenant scoping + UI reconciliation — summary of changes #18

Description

@KDwevedi

Tracking issue summarizing the authentication/authorization work and the UI reconciliation landed on main (PRs #13#17). Recorded here for reviewers and future contributors.

Added

  • Backend auth module (pipeline/auth/): Keycloak OIDC JWT validation (RS256 against realm JWKS, iss + audience docs-pipeline-api, exp required, clock-skew leeway). Realm/resource roles → a Permission enum; instances / envs token claims for multi-tenant scoping. FastAPI dependency aliases (RequireUpload/Review/Pipeline/Search/Admin/ManageUsers, CurrentUser).
  • Role → permission model: master_admin/admin = all (instance-unrestricted); content_curator = upload/review/pipeline/search; viewer = search.
  • Keycloak in the stack: keycloak + keycloak-db services in docker-compose.yml (:8082, relative path /auth), realm-import mount, and scripts/keycloak_bootstrap_docs_pipeline.py (idempotent role/client/mapper/user bootstrap).
  • Frontend OIDC: keycloak-js Authorization-Code + PKCE login, AuthProvider/useAuth, silent SSO + token refresh, Bearer injection on all API calls, ?access_token= for element (PDF/embed) loads, and permission-gated nav/actions.
  • .env.example auth block and a README "Authentication (Keycloak)" deployment section.

Changed / hardened

  • Every route now gated — all previously-open read/introspection endpoints require auth; only GET /health is public. A guardrail test asserts every route is gated or on an explicit public allowlist.
  • Tenant scoping: document-scoped reads/mutations enforce instance access and return 404 (not 403) on cross-tenant access to avoid leaking other tenants' IDs.
  • Admins are instance-unrestricted even with a scoped instances claim.
  • Marqo tenant-scoping is forward-ready and tolerant: stamps instance on ingest and filters searches when the field exists, but never recreates or errors on a legacy index (single-tenant deploys keep working unchanged).
  • Backward compatible: AUTH_DISABLED=true (default) accepts a synthetic local admin, so existing/local deploys work with no login; auth is opt-in via config only.

Removed

Anonymization (public-repo hygiene)

  • Generic placeholders throughout: realm/client names, *.example.com domains, generic storage paths, and neutral tenant sample values in tests/bootstrap/docs. No secrets are committed (public/bearer-only clients; credentials come from the environment at runtime).

PRs

Follow-ups

  • manage_users master-admin user-management APIs.
  • Dev/prod per-document enablement matrix.
  • Seed the bootstrap script with example role fixtures.
  • Multi-tenant Marqo index migration (only needed when >1 tenant).

🤖 Generated with Claude Code

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions