feat(provisioning): tenant data model + CRUD with JWT-validated authz
Implements Phase 3 from docs/NEXT-STEPS.md. Mongoose schemas (services/provisioning/src/schemas/): - Tenant: slug, name, status, plan, domains, billingInfo, plus handles for Authentik group, OCIS space, and Stalwart domain (set in Phase 4) - User: authentikSubjectId, tenantIds[], email, name, role, platformAdmin flag - Subscription: tenantId, plan, status, Stripe IDs (unused until Phase 4) Auth (services/provisioning/src/auth/): - JwtAuthGuard verifies Authentik access tokens against the provider's JWKS with issuer + audience checks. Uses NODE_EXTRA_CA_CERTS to trust the mkcert root for the local Authentik cert - ActorService resolves the verified JWT into a Mongo User document — every controller reads tenantIds + platformAdmin from the DB, not the token - CurrentUser decorator extracts the JWT payload onto controllers CRUD modules: - /tenants, /users, /subscriptions with create/read/update/delete - /users/me upserts the caller's User record on every request, syncing email, name, tenantIds, and platformAdmin from the JWT's groups claim — the only place we read JWT.groups outside the bootstrap Why DB-derived authz: putting all group memberships in the JWT doesn't scale past ~50 tenants per user (header/cookie size limits, no mid-session revocation, stale data until re-login). JWT now carries identity only; the DB is the source of truth for who can see what. Seed (SeedService.OnApplicationBootstrap): idempotent creation of the default 'dezky' tenant + matching subscription. User records are created on first /users/me hit. Infrastructure: - Traefik label exposes provisioning at https://api.dezky.local (dev only) - api.dezky.local added to Docker network aliases on Traefik - mkcert root CA mounted into the provisioning container for JWKS fetch - Authentik 'groups' scope mapping created + attached to dezky-portal provider; portal now requests it as a scope - nuxt.config.ts portal: exposeAccessToken=true so Nitro forwards token; NUXT_OIDC_TOKEN_KEY fixed to base64-encoded 32 bytes (was hex, causing "Invalid key length" once exposeAccessToken turned on) Portal: apps/portal/server/api/me.get.ts is a scaffolding route that forwards the user's access token to provisioning and returns profile + tenants + subscriptions — verifies the full chain end to end.
This commit is contained in:
@@ -54,6 +54,7 @@ services:
|
||||
- traefik.dezky.local
|
||||
- auth.dezky.local
|
||||
- app.dezky.local
|
||||
- api.dezky.local
|
||||
- files.dezky.local
|
||||
- mail.dezky.local
|
||||
- office.dezky.local
|
||||
@@ -331,11 +332,23 @@ services:
|
||||
STALWART_ADMIN_USER: admin
|
||||
STALWART_ADMIN_PASSWORD: ${STALWART_ADMIN_PASSWORD}
|
||||
OCIS_API_URL: http://ocis:9200
|
||||
# JWT validation against Authentik for portal-issued access tokens
|
||||
AUTHENTIK_ISSUER: https://auth.dezky.local/application/o/dezky-portal/
|
||||
AUTHENTIK_AUDIENCE: dezky-portal
|
||||
AUTHENTIK_JWKS_URI: https://auth.dezky.local/application/o/dezky-portal/jwks/
|
||||
# Trust mkcert root CA for Node fetch (dev only)
|
||||
NODE_EXTRA_CA_CERTS: /etc/ssl/mkcert-root.pem
|
||||
volumes:
|
||||
- ../../services/provisioning:/app
|
||||
- provisioning_node_modules:/app/node_modules
|
||||
- ./certs/mkcert-root.pem:/etc/ssl/mkcert-root.pem:ro
|
||||
networks: [dezky]
|
||||
depends_on:
|
||||
mongo:
|
||||
condition: service_healthy
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.api.rule=Host(`api.dezky.local`)
|
||||
- traefik.http.routers.api.tls=true
|
||||
- traefik.http.services.api.loadbalancer.server.port=3001
|
||||
|
||||
|
||||
Reference in New Issue
Block a user