T-HUB/docs/design/2026-10-13-files-oidc-sso.md
glass beadian b8671113f5 docs: publish governance, governed boosts, receipts, OIDC phase 2, files SSO
Engineering guide: session/refresh/logout architecture, publish-governance
and amplify gating sections, file map to migrations 001-034. Admin guide:
governance administration, files-SSO operations, verification harnesses,
migration list + deploy order + 034 session note. User manual: calendar
receipt badges, boost gating, connections governance note (replaces the
stale 'stub' statuses). Design doc 2026-10-13: marked implemented with the
as-shipped phase 2. Wiki (EN/ES): Drive sign-in + boosts/approvals.
README: governance + Files SSO bullets.
2026-10-14 11:52:48 +00:00

106 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Files SSO design — Hub as the OIDC issuer for oCIS
Status: **implemented** (phase 1: provider + sign-in, shipped; phase 2: session-bound
refresh tokens + logout propagation, shipped — see §Phases update below)
Date: 2026-10-13
Related: docs/design/2026-10-12-collectives-architecture.md, brand review P0 #2
(files.occupymedia.cc navy login wall), matrix-sso module (chat SSO, shipped).
## Problem
The Hub's Drive view embeds oCIS web (files.occupymedia.cc). Today:
- No Hub user has an oCIS account. The API reaches oCIS through one shared
service account (`FILES_OCID_USER`); the collective Library lives in that
account's space.
- The embedded oCIS web shows its own sign-in screen for a login the user
does not have, in ownCloud's default navy/white theme — the single worst
brand break measured in the review.
## Decision
The Hub becomes the OIDC identity provider; oCIS trusts it as its external
issuer. This mirrors what just shipped for chat: the Hub is already the
identity provider for Matrix (JWT broker). Files becomes the second relying
party of the same identity, not a third account system.
Rejected alternative: a standalone IdP (Authelia/Authentik/Keycloak). It adds
a service to run and a second admin surface for one relying party; the Hub
already holds the session, the RBAC roles, and the username namespace.
## Flow (target state)
1. User opens `/files/drive` in the Hub. Their Hub session cookie proves who
they are.
2. The embedded oCIS web redirects to `https://api.occupymedia.cc/oidc/authorize`
(client id `web`, PKCE required).
3. The Hub is already signed in → no login prompt, no consent screen for this
first-party client (auto-approve). A short-lived code returns to oCIS.
4. oCIS exchanges the code at `/oidc/token`, reads claims
(`preferred_username` = Hub username, `email`, `name`), and provisions the
oCIS account on first login (oCIS does this natively when
`OCIS_OIDC_ISSUER` points at an external issuer).
5. The user lands in their own drive. The navy wall is gone because there is
no wall.
## What the Hub must add
- `apps/api/src/oidc/`: an OIDC provider module —
`/.well-known/openid-configuration`, `/oidc/authorize`, `/oidc/token`,
`/oidc/userinfo`, `/oidc/jwks`. Signing keys in the existing vault pattern
(ES256 or RS256, rotated like DEKs). Session binding to the existing Hub
session cookie; codes single-use, ≤60 s TTL, PKCE S256 enforced.
- Auto-consent policy: client id `web` (the oCIS web app) is first-party and
skips the consent screen. Any other client id requires explicit consent —
fail closed.
- Claim mapping: `preferred_username` must equal the Hub username verbatim
(same invariant the Matrix SSO module already enforces: Hub usernames are a
subset of the localpart grammar, case-folded identity is refused).
## What oCIS must change
- `OCIS_OIDC_ISSUER=https://api.occupymedia.cc/oidc`
- `WEB_OIDC_CLIENT_ID=web`, scope `openid profile email`
- oCIS auto-provisions accounts from claims (default behaviour with an
external issuer). The `FILES_OCID_USER` service account stays for the
Library's WebDAV path.
## Phases
1. **Provider + sign-in** (≈1–2 days): the module above; Drive view switches
`authMode` to `"oidc"`; the navy wall disappears. **Shipped.**
2. **Personal drives** (≈1 day): uploaded files land in the user's own drive;
the Library keeps serving collective folders from the service account. **Shipped** (autoprovisioned personal spaces verified live).
3. **Share-through** (≈1 day, optional): group folders shared into members'
drives so the Drive tab also shows collective Library content; only then
consider deprecating the Library tab. *(open)*
## Phase 2 as shipped (refresh + logout, migration 034)
The security note below said logout propagation was a nice-to-have; it shipped
as part of hardening instead:
- `auth_sessions` rows (`sid` in the session payload): login/reissue write the
row, `POST /auth/logout` revokes it, `reissue` refuses revoked or pre-034
tokens. Request validation stays stateless.
- `oidc_refresh_tokens`: opaque tokens, SHA-256 at rest, rotate-on-use,
`family_id` chains with reuse detection (presenting a consumed token revokes
the chain). Every refresh re-checks the session row and active membership —
so an oCIS session can never outlive the Hub sign-in that minted it, and
logout propagates within one access-token TTL (≤1 h).
- The token endpoint is unauthenticated: lookups go through the definer-rights
`oidc_refresh_lookup()` (audit_append pattern). Refresh TTL has no constant —
it *is* the Hub session's remaining lifetime.
- Matrix SSO stays out of propagation scope by design: the broker mints
2-minute `m.login.token`s and Element holds a Matrix-native session after.
## Security notes
- The OIDC provider must never accept the service-account identity for a
browser session; tokens are minted only from a live Hub session.
- Logout: Hub logout revokes the session cookie; oCIS tokens expire on their
own TTL (oCIS refresh behaviour is issuer-driven). Single-logout signaling
to oCIS is a nice-to-have, not a blocker.
- The issuer URL must be exactly `https://api.occupymedia.cc/oidc` everywhere
(issuer discovery, oCIS config, token audience) — mismatch breaks token
validation.