Skip to content

Core · Service · Active

platform-auth-service

Shared auth and session service for the workspace, owning cookie sessions, provider configuration, local credentials, OIDC login and trusted identity routes for product backends.

  • TypeScript
  • NestJS 11
  • PostgreSQL
  • JWT/JWKS
  • cookie-parser

Spec sheet

Boundary

Core / Auth

Runtime

NestJS 11 HTTP service

Default port

3100

Persistence

Dedicated auth-postgres via AUTH_DATABASE_URL

Exposure

Public auth surface plus internal trusted routes

Responsibilities

  • Issue and resolve shared HttpOnly sessions across products.
  • Expose provider discovery, CSRF bootstrap and password login.
  • Support OIDC provider start and callback flows.
  • Manage provider configuration and local credentials through guarded admin routes.
  • Provide internal identity, membership and session orchestration routes.
  • Expose JWKS and public key material for token validation.

Interfaces and contract surface

  • GET /health
  • GET /auth/runtime
  • GET /.well-known/jwks.json
  • GET /auth/providers
  • GET /auth/session
  • GET /auth/csrf
  • POST /auth/login/password
  • POST /auth/logout
  • GET /auth/oidc/:providerId/start
  • GET /auth/oidc/:providerId/callback
  • GET /auth/admin/providers
  • GET /auth/admin/providers/:providerId
  • PUT /auth/admin/providers/:providerId
  • GET /auth/admin/local-credentials
  • PUT /auth/admin/local-credentials/:subjectId
  • DELETE /auth/admin/local-credentials/:subjectId
  • GET /internal/public-key.pem
  • POST /internal/identities/upsert
  • POST /internal/session/resolve
  • POST /internal/session/issue
  • POST /internal/session/bootstrap
  • DELETE /internal/memberships/by-subject

Consumers

Dependencies and external touchpoints

Notes

  • Auth state is Postgres-only; the previous file-state fallback has been removed.
  • The runtime is split across public, admin and internal route surfaces to keep responsibilities isolated.
  • A bare auth-service startup does not preload local users; product backends may bootstrap identities and credentials via internal routes.

Source references

  • platform-auth-service/README.md
  • docs/core-services-integration.md
  • platform-auth-service/package.json

Come integrarsi davvero

Gli esempi sotto servono per smoke test e integrazione backend-to-core. Nel flusso applicativo normale il frontend deve continuare a chiamare il proprio backend/BFF same-origin su /api/auth/*, non auth.cs.lvh.me direttamente.

Persistenza runtime

platform-auth-service usa PostgreSQL via AUTH_DATABASE_URL e persiste lo stato auth in tabelle dedicate:

  • platform_auth_identities
  • platform_auth_accounts
  • platform_auth_memberships
  • platform_auth_capabilities
  • platform_auth_capability_grants
  • platform_auth_provider_configs
  • platform_auth_signing_keys

La tabella legacy platform_auth_state con payload JSONB unico non viene piu letta, scritta, migrata o droppata dal runtime. Le membership hanno colonne interrogabili per product_code, subject_id, tenant_id, role e status; solo gli attributi estensibili restano in attributes JSONB.

I token di sessione propagano anche tenantId, role, permissions legacy e capabilities runtime. Le capability sono default-deny: senza grant esplicito la lista capabilities e vuota e i BFF non devono abilitare funzioni CORE o tool MCP. Il ruolo SUPER_ADMIN continua a emettere la permission trasversale platform:* per compatibilita, ma non abilita automaticamente le capability.

Le capability sono record generici (code, serviceKey, kind, riskLevel, metadata, enabled) e i grant sono assegnati per productCode a uno scope role o membership. Gli override membership possono usare deny, che prevale sugli allow del ruolo.

Variabili utili

bash
export AUTH_PUBLIC_URL=http://auth.cs.lvh.me:8080
export AUTH_INTERNAL_URL=http://127.0.0.1:3100
export INTERNAL_TOKEN=platform-local-stack-internal-token

1. Bootstrap identity, membership e credenziale locale

platform-auth-service non parte con un utente locale pre-caricato. Se stai testando il servizio in isolamento, crea prima identity, membership e password tramite la surface interna.

bash
curl -sS -X POST "$AUTH_INTERNAL_URL/internal/identities/upsert" \
  -H "Content-Type: application/json" \
  -H "x-platform-internal-token: $INTERNAL_TOKEN" \
  -d '{
    "email": "admin@local.test",
    "username": "admin@local.test",
    "password": "ChangeMe123!",
    "enabled": true,
    "membership": {
      "productCode": "funnel-ia-engine",
      "subjectId": "bootstrap-admin",
      "role": "SUPER_ADMIN"
    }
  }'

Nei prodotti reali questo bootstrap viene orchestrato dal backend di prodotto. Un riferimento concreto e funnel-ia-engine/backend/src/auth/auth.service.ts, che sincronizza utenti locali verso /internal/identities/upsert.

Questo e il flusso piu utile per verificare che il servizio emetta davvero la sessione cookie-based condivisa. Le POST pubbliche richiedono double-submit CSRF: prima chiama /auth/csrf, poi invia il token come X-CSRF-Token insieme al cookie jar.

bash
COOKIE_JAR=/tmp/platform-auth.cookies
rm -f "$COOKIE_JAR"

CSRF_TOKEN=$(
  curl -sS -c "$COOKIE_JAR" "$AUTH_PUBLIC_URL/auth/csrf" \
    | node -e "let body=''; process.stdin.on('data', chunk => body += chunk); process.stdin.on('end', () => console.log(JSON.parse(body).csrfToken));"
)

curl -i -sS -X POST "$AUTH_PUBLIC_URL/auth/login/password" \
  -H "Content-Type: application/json" \
  -H "X-CSRF-Token: $CSRF_TOKEN" \
  -b "$COOKIE_JAR" \
  -c "$COOKIE_JAR" \
  -d '{
    "email": "admin@local.test",
    "password": "ChangeMe123!",
    "productCode": "funnel-ia-engine"
  }'

La risposta JSON contiene anche accessToken, ma per validare il comportamento reale conviene poi leggere la sessione usando il cookie appena emesso:

bash
curl -sS "$AUTH_PUBLIC_URL/auth/session" \
  -b "$COOKIE_JAR"

3. Emissione e risoluzione sessione dal backend di prodotto

Quando il prodotto ha gia autenticato o provisionato un proprio soggetto, il backend puo chiedere a platform-auth-service di emettere o risolvere una sessione per quello specifico productCode / subjectId.

Emissione token:

bash
curl -sS -X POST "$AUTH_INTERNAL_URL/internal/session/issue" \
  -H "Content-Type: application/json" \
  -H "x-platform-internal-token: $INTERNAL_TOKEN" \
  -d '{
    "productCode": "funnel-ia-engine",
    "subjectId": "bootstrap-admin",
    "authProvider": "local"
  }'

Bootstrap sessione con sostituzione membership di prodotto:

bash
curl -sS -X POST "$AUTH_INTERNAL_URL/internal/session/bootstrap" \
  -H "Content-Type: application/json" \
  -H "x-platform-internal-token: $INTERNAL_TOKEN" \
  -d '{
    "email": "admin@local.test",
    "username": "admin@local.test",
    "authProvider": "local",
    "memberships": [
      {
        "productCode": "cs-portal",
        "subjectId": "portal-admin",
        "role": "PORTAL_ADMIN"
      }
    ]
  }'

Risoluzione del token appena ricevuto:

bash
curl -sS -X POST "$AUTH_INTERNAL_URL/internal/session/resolve" \
  -H "Content-Type: application/json" \
  -H "x-platform-internal-token: $INTERNAL_TOKEN" \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -d '{
    "productCode": "funnel-ia-engine"
  }'

Per backend e facade pubbliche, il pattern corretto resta:

  • frontend -> /api/auth/* same-origin
  • backend prodotto -> platform-auth-service
  • header tecnico obbligatorio x-platform-internal-token
  • Authorization: Bearer <session-token> inoltrato solo come contesto utente delegato, mai come sostituto del token interno

4. Admin provider e credenziali locali

Le viste operations e i BFF amministrativi usano la surface admin protetta da token interno:

bash
curl -sS "$AUTH_INTERNAL_URL/auth/admin/providers" \
  -H "x-platform-internal-token: $INTERNAL_TOKEN"
bash
curl -sS "$AUTH_INTERNAL_URL/auth/admin/local-credentials?productCode=cs-portal" \
  -H "x-platform-internal-token: $INTERNAL_TOKEN"

Surface capability:

bash
curl -sS "$AUTH_INTERNAL_URL/auth/admin/capabilities" \
  -H "x-platform-internal-token: $INTERNAL_TOKEN"

curl -sS "$AUTH_INTERNAL_URL/auth/admin/effective-capabilities?productCode=cs-chat&subjectId=<SUBJECT_ID>" \
  -H "x-platform-internal-token: $INTERNAL_TOKEN"

Workspace reference: /Users/jeanpaul/projects/cs-repository