feat(portal): real OCIS storage data via refresh-token service auth

The Storage page + endpoint landed earlier but had no working OCIS
backend credential. OCIS has no service-account/client-credentials grant
and trusts a single issuer, and basic auth resolves no user in our
external-IdP setup — so authenticate OcisClient via an OIDC
refresh-token bootstrap instead:

- One-time headless login of svc-platform-api against the ocis provider
  (public client ocis-web, issuer .../o/ocis/) yields a refresh token,
  persisted in Mongo (ocis_credentials) and rotated on every use.
- OcisClient mints access tokens with the refresh_token grant; the
  service user holds the OCIS admin role (OCIS_ADMIN_USER_ID) so
  libregraph ListAllDrives works.
- scripts/bootstrap-ocis.mjs re-runs the bootstrap if the token lapses.
- Dashboard Plan card gains a storage capacity bar beside seats;
  hidden when storage is unavailable.
- compose + .env.example: OCIS service OIDC env and admin user id.
- docs/NEXT-STEPS: document the mechanism and the dead-end alternatives.
This commit is contained in:
Ronni Baslund
2026-05-31 21:29:17 +02:00
parent 559348f6bc
commit f8618b2bbc
8 changed files with 335 additions and 60 deletions
+23 -4
View File
@@ -124,10 +124,29 @@ Need a minimal JMAP client that wraps `Domain/set` (create), `Domain/get`
via the persistent admin's bearer token from the OAuth flow we already use
for the web UI.
**OCIS (libregraph)**`POST /graph/v1.0/drives` with body
`{ "name": "<slug>", "driveType": "project" }`. Needs service-to-service
auth: either an OIDC client_credentials grant (requires registering a new
Authentik provider for the worker) or the IDM admin user's bearer token.
**OCIS (libregraph)**space *provisioning* is still stubbed:
`POST /graph/v1.0/drives` with body `{ "name": "<slug>", "driveType":
"project" }` to create a tenant's project space, then assign it.
**OCIS read auth (done — powers the customer-admin Storage page).** OCIS has
*no* backend service-account/client-credentials grant and trusts exactly one
issuer, and basic auth doesn't resolve a user in our external-IdP setup. The
working mechanism is a **refresh-token bootstrap**:
1. A dedicated Authentik user `svc-platform-api` (with an email — OCIS
autoprovision rejects empty emails) logs in **once** against the *ocis*
provider (public client `ocis-web`, per-provider issuer `.../o/ocis/` — the
one OCIS trusts). Run it headlessly:
`docker compose exec platform-api node /app/scripts/bootstrap-ocis.mjs`.
The refresh token is persisted in Mongo (`ocis_credentials`).
2. `OcisClient` mints access tokens with the `refresh_token` grant and persists
the rotated token each call (Authentik rotates on every use).
3. The svc user needs the OCIS **admin** role for `ListAllDrives` — granted via
`OCIS_ADMIN_USER_ID=<svc OCIS account UUID>` on the ocis service.
Note: the "global" issuer mode is **not** an option — its issuer is the
Authentik root, which has no `.well-known/openid-configuration`, so OCIS can't
validate tokens against it.
### Authentik API examples (for the eventual user-creation flow)