L’Invariante
Una credenziale, un confine. Una credenziale emessa per una superficie non funziona mai silenziosamente su un’altra: le key edge guidano il canale sync, le sessioni PIN guidano le azioni kernel, le sessioni portale guidano l’API tenant, le sessioni admin guidano le operations. Il least privilege è strutturale — un token bridge fisicamente non può aprire un lotto.
Il Contratto
| Credenziale | Emessa da | Valida su | Scope |
|---|---|---|---|
| Sessione PIN operatore | POST /api/v1/auth/login (edge) |
/api/v1/actions/*, /api/v1/auth/*, /api/v1/audit/verify |
role-scoped (permissions del manifest) |
| Edge API key (bootstrap claim) | POST /api/v1/bootstrap/claim |
/api/v1/sync/* |
sync (default) / full |
| Sessione portale | POST /api/v1/portal/login o claim invito pi_* |
/api/v1/tenant/*, /api/v1/portal/* |
owner / member |
| Sessione admin | POST /api/v1/admin/login |
/api/v1/admin/* |
solo ops, ogni mutazione audit-loggata |
Registro ruoli (manifest production-line): MAGAZZINIERE (trigger di recezione + letture), OPERATORE (trigger di lavorazione), QUALITA (trigger di collaudo), ADMIN (wildcard). I ruoli vivono nel rbac_engine del manifest — ogni verticale dichiara i propri.
Il Flusso
edge: PIN → /auth/login → sessione (TTL 12h, ax_sessions, revocabile)
PIN condiviso? → 409 PIN_AMBIGUOUS {candidates:[{operatorId,name}]}
→ rimanda con operatorId
cloud: email+password → /portal/login → sessione (12h)
email su >1 tenant? → 401 TENANT_AMBIGUOUS → passa tenant_id
sync: token bt_* monouso → /bootstrap/claim → {tenant_id, api_key}
il re-claim RUOTA la key — la vecchia muore all'istante
Esempio di Payload
# Sessione operatore edge
curl -s http://edge:8291/api/v1/auth/login -H 'content-type: application/json' \
-d '{"tenantId":"<uuid>","deviceCode":"IPC-01","pin":"1234"}'
# → {"token":"…","expiresInHours":12,"context":{"tenantId":"…","operatorId":"…","role":"MAGAZZINIERE","deviceId":"IPC-01"}}
# Sessione portale cloud
curl -s https://cp.axiom-trace.eu/api/v1/portal/login -H 'content-type: application/json' \
-d '{"email":"qa@customer.com","password":"…"}'
# → {"token":"…","tenant_id":"…","role":"owner","display_name":"…"}
I Fallimenti Noti
| Sintomo | Causa → fix |
|---|---|
409 PIN_AMBIGUOUS |
PIN condiviso → rimanda con operatorId dai candidates |
401 TENANT_AMBIGUOUS |
Email su più tenant → passa tenant_id esplicito |
401 su /tenant/* con ak_* |
Scope della key è sync → la superficie portale vuole una sessione portale o key full |
403 con login valido |
Binding origine (portal_origin) o tetto di ruolo (member vs rotta owner-only) |
Login ok, azioni 401 |
TTL sessione 12h — rifai login; mai cachare token oltre il TTL |