ADR-0010 — OIDC + tenant-scoped RBAC; tokens for programmatic access
- Status: Accepted
- Date: 2026-06-11
- Related: 03 FR-A / NFR-6, 05 Identity, ADR-0007, ADR-0011
Context
BitVault serves browsers (web), automation (CLI/API), and enterprise SSO. It must authenticate humans and machines, authorize tenant-scoped and resource-scoped actions, and feed tenant context to the DB isolation layer (ADR-0007). It is self-hostable, so we cannot mandate a specific cloud identity provider.
Decision
Authentication
- Self-host baseline: email/password with a strong adaptive hash (argon2id), email verification, and secure session cookies for the web app.
- SSO/enterprise & SaaS: OIDC (Authorization Code + PKCE) against external IdPs; pluggable, not hard-coded to one vendor.
- Programmatic access: revocable API tokens / PATs (CLI, automation), stored hashed, scoped, with expiry. The CLI dogfoods these (H2).
- Internally, requests carry a signed access token (JWT or opaque + introspection)
conveying
tenant_id,user_id, roles; the gateway authenticates once and propagates an auth context through gRPC metadata.
Authorization
- Tenant-scoped RBAC: roles
owner | admin | member | viewerat the tenant level, plus resource grants (per-node shares) resolved with the Sharing context. Decision = role capability ∪ resource grants, default-deny. - The resolved
tenant_idsets the DB tenant context for RLS (ADR-0007) — authz and isolation are wired together, not independent. - Authz decisions for hot paths are cached in Redis with short TTL + explicit invalidation on grant changes.
Consequences
Positive
- Works for humans, machines, and SSO without coupling to a cloud vendor (self-host friendly).
- One auth context flows from edge → services → DB isolation (coherent security story; closes app/DB boundary gaps with ADR-0007).
- Token model dogfoods the public API via the CLI.
Negative / costs
- Running our own password auth means owning its security (hashing, reset flows, lockout, breach response) — real work; mitigated by using vetted libraries and defaulting orgs to SSO where possible.
- RBAC + per-resource grants is more complex than flat roles; necessary for sharing (FR-D). Kept default-deny and centralized in Identity+Sharing.
- JWT revocation needs care (short TTL + introspection/denylist) so revoked tokens don’t linger.
Alternatives considered
- Delegate all auth to an external IAM (e.g. Keycloak/Auth0) as a hard dependency: rejected as a requirement — bad for lightweight self-host; OIDC support makes these optional integrations instead.
- Capability tokens / macaroons everywhere: interesting for scoped sharing; overkill as the primary model now. Presigned URLs already provide scoped, time-boxed capabilities for the data plane (ADR-0011).
- Flat roles only (no per-resource grants): rejected — cannot express sharing (FR-D).