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
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
pipeline/auth/): Keycloak OIDC JWT validation (RS256 against realm JWKS,iss+ audiencedocs-pipeline-api,exprequired, clock-skew leeway). Realm/resource roles → aPermissionenum;instances/envstoken claims for multi-tenant scoping. FastAPI dependency aliases (RequireUpload/Review/Pipeline/Search/Admin/ManageUsers,CurrentUser).master_admin/admin= all (instance-unrestricted);content_curator= upload/review/pipeline/search;viewer= search.keycloak+keycloak-dbservices indocker-compose.yml(:8082, relative path/auth), realm-import mount, andscripts/keycloak_bootstrap_docs_pipeline.py(idempotent role/client/mapper/user bootstrap).keycloak-jsAuthorization-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.exampleauth block and a README "Authentication (Keycloak)" deployment section.Changed / hardened
GET /healthis public. A guardrail test asserts every route is gated or on an explicit public allowlist.instancesclaim.instanceon ingest and filters searches when the field exists, but never recreates or errors on a legacy index (single-tenant deploys keep working unchanged).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
ui/src/styles/appStyles.jsandui/src/markdown.cssin favor of the themed component set.feature/auth-backend-gates,feature/domain-tags-pr,bh-main.Anonymization (public-repo hygiene)
*.example.comdomains, 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
?access_token=· Wire Keycloak OIDC (PKCE) login + Bearer auth into the maintainer UI #16 UI OIDC · Restore polished maintainer UI (keep box styling) + keep OIDC auth #17 restore polished UI + keep auth.Follow-ups
manage_usersmaster-admin user-management APIs.🤖 Generated with Claude Code