Phase 1 kills the navy login wall (provider + PKCE + auto-consent for the first-party oCIS web client); phases 2-3 move uploads into personal drives and share collective folders into them. Mirrors the Matrix JWT broker: the Hub is the identity, oCIS is a relying party.
4 KiB
Files SSO design — Hub as the OIDC issuer for oCIS
Status: proposed (design pass; not implemented) 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)
- User opens
/files/drivein the Hub. Their Hub session cookie proves who they are. - The embedded oCIS web redirects to
https://api.occupymedia.cc/oidc/authorize(client idweb, PKCE required). - 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.
- 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 whenOCIS_OIDC_ISSUERpoints at an external issuer). - 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_usernamemust 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/oidcWEB_OIDC_CLIENT_ID=web, scopeopenid profile email- oCIS auto-provisions accounts from claims (default behaviour with an
external issuer). The
FILES_OCID_USERservice account stays for the Library's WebDAV path.
Phases
- Provider + sign-in (≈1–2 days): the module above; Drive view switches
authModeto"oidc"; the navy wall disappears. - Personal drives (≈1 day): uploaded files land in the user's own drive; the Library keeps serving collective folders from the service account.
- 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.
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/oidceverywhere (issuer discovery, oCIS config, token audience) — mismatch breaks token validation.