From 467e6a7ab5c77129ca73afdb525e7b7dd1751624 Mon Sep 17 00:00:00 2001 From: Ronni Baslund Date: Sun, 24 May 2026 00:07:40 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20mark=20Phase=204=20partial=20=E2=80=94?= =?UTF-8?q?=20Authentik=20real,=20Stalwart=20+=20OCIS=20stubbed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Honest status for the data-model + provisioning phases. Lists the smoke test that verified the chain works end-to-end and points at the upstream docs for the JMAP and libregraph follow-up work. --- docs/NEXT-STEPS.md | 74 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/docs/NEXT-STEPS.md b/docs/NEXT-STEPS.md index 1998f8a..c9c3bc2 100644 --- a/docs/NEXT-STEPS.md +++ b/docs/NEXT-STEPS.md @@ -68,24 +68,70 @@ Goal: Users can log in to the portal via Authentik. - The `dezky` group in Authentik is the single tenant for dev. New tenants in Phase 4 need to create matching Authentik groups - A `dezky-platform-admins` group doesn't exist yet — for now akadmin's membership in `authentik Admins` does NOT grant platform-admin rights. Create that group if you want admin-only endpoints to work for you -## Phase 4: Provisioning automation (week 2-3) +## Phase 4: Provisioning automation (week 2-3) — partial -Goal: Sign up creates tenant resources across all services. +Orchestration ships, two of three integrations are still stubs pending +upstream-specific work. -- [ ] Endpoint: `POST /tenants` — creates tenant in MongoDB -- [ ] Worker: triggers Authentik tenant/group creation via API -- [ ] Worker: configures Stalwart domain + DKIM via admin API -- [ ] Worker: creates OCIS space -- [ ] Worker: emails customer with onboarding info +- [x] `POST /tenants` writes tenant and triggers reconciliation in one call +- [x] `POST /tenants/:slug/reconcile` retries provisioning for an existing + tenant — idempotent, useful when an upstream was down or external + state drifted +- [x] Per-step state recorded on `Tenant.provisioningStatus` (ok / skipped / + error / pending) + `Tenant.provisioningErrors` for the last failure + message; tenant auto-activates when all steps settle +- [x] Worker: Authentik group creation (real, idempotent) +- [ ] Worker: Stalwart domain + DKIM (stubbed — v0.16 dropped REST in favor + of JMAP, see follow-up below) +- [ ] Worker: OCIS space (stubbed — needs libregraph `/drives` endpoint + with service-to-service auth) +- [ ] Worker: onboarding email (no SMTP wired yet) + +### Where things live + +| Concern | File | +|---|---| +| Integration clients | `services/provisioning/src/integrations/{authentik,stalwart,ocis}.client.ts` | +| Orchestration | `services/provisioning/src/tenants/provisioning.service.ts` | +| `/tenants/:slug/reconcile` | `services/provisioning/src/tenants/tenants.controller.ts` | +| Portal proxy routes | `apps/portal/server/api/tenants/index.post.ts` + `[slug]/reconcile.post.ts` | + +### Quick smoke test + +From the portal in the browser (signed in), in DevTools: + +```js +// Create a fresh tenant +await fetch('/api/tenants', { + method: 'POST', + headers: {'Content-Type':'application/json'}, + body: JSON.stringify({ slug: 'acme', name: 'Acme Co', plan: 'pro' }) +}).then(r => r.json()) + +// Re-run provisioning (idempotent) +await fetch('/api/tenants/acme/reconcile', { method: 'POST' }).then(r => r.json()) +``` + +Response should include `provisioningStatus: { authentik: 'ok', stalwart: +'skipped', ocis: 'skipped' }` and `status: 'active'`. Verify the Authentik +group exists via the admin UI at `/if/admin/#/identity/groups`. + +### Stub follow-up work + +**Stalwart (JMAP)** — v0.16 [moved management off REST](https://stalw.art/docs/api/management/overview). +Need a minimal JMAP client that wraps `Domain/set` (create), `Domain/get` +(idempotency check), `Principal/set` (DKIM-keyed signing identity). Auth +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": "", "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. + +### Authentik API examples (for the eventual user-creation flow) -Authentik API examples: ```typescript -// Create group (tenant) in Authentik -await authentikClient.coreGroupsCreate({ - name: tenant.slug, - attributes: { tenantId: tenant.id, plan: tenant.plan }, -}) - // Create user await authentikClient.coreUsersCreate({ username: user.email,