Accesso singolo
L’accesso singolo consente a un provider di identità esterno (IdP) di decidere chi può accedere al tuo server Control Center. Sono supportati due protocolli — SAML 2.0 e OpenID Connect — ed entrambi approdano nello stesso punto: una credenziale del dispositivo appena coniata, indistinguibile da un invito o da un abbinamento tramite QR in ogni passaggio a valle.
La forma di un accesso
Sezione intitolata “La forma di un accesso”Gli accessi SAML sono mediati dal browser. L’IdP non apre mai una connessione verso il tuo server; è il browser dell’utente a trasportare tutto:
browser ──> /saml/login ──> IdP ──POST SAMLResponse──> /saml/acs │ verify ▼ provision user (maybe) │ ▼ browser handoff: device PSK in the URL fragment (never a query parameter)Poiché è il browser a fare da corriere, per gli accessi un server dietro NAT funziona senza problemi: localhost, LAN, Tailscale, VPN o un tunnel vanno tutti bene — solo il browser dell’utente deve raggiungere l’URL ACS. Il relay WebSocket usato dal controllo remoto da telefono non può trasportare il round-trip HTTP, ed è per questo che i punti di ingresso SSO puntano sempre all’origine HTTP del server.
OpenID Connect segue la stessa forma con un round-trip authorization-code + PKCE; i claim vengono letti dall’endpoint del token dell’issuer su TLS, quindi il browser non fornisce mai dati di identità.
Gli adattatori
Sezione intitolata “Gli adattatori”Control Center separa le responsabilità in modo deliberato:
| Componente | Dove | Di cosa si occupa |
|---|---|---|
cc_saml nativo |
packages/cc_natives/native/saml/ |
Verifica XML-DSig, canonicalizzazione, convalida del profilo SAML — delegate al crate saml in puro Rust fissato, dietro un’ABI C senza stato |
SamlService / OidcService |
cc_server_core |
stato del login (tracker delle richieste in sospeso, deduplica dei replay), parametri della policy di convalida, la chiamata di provisioning condivisa |
SsoProvisioner |
cc_server_core |
trova-o-crea l’utente, fissa il subject del provider, concede l’appartenenza secondo la policy di appartenenza automatica |
ScimService |
cc_server_core |
la superficie push SCIM 2.0 (ciclo di vita di provisioning/deprovisioning) |
SsoSettingsService |
cc_server_core |
le righe delle connessioni salvate, la riapplicazione a caldo, il token SCIM |
Di proposito non esiste alcun fallback crittografico SAML in Dart puro:
è proprio nell’XML-DSig e nella canonicalizzazione fatti a mano che vivono le
vulnerabilità di signature wrapping, quindi una libreria cc_saml mancante
rifiuta sia l’accesso SAML sia l’avvio del server. Il nativo è senza stato per
scelta progettuale — lo stato tra richieste vive nei servizi Dart, monouso e
di breve durata.
Cosa verifica un accesso
Sezione intitolata “Cosa verifica un accesso”All’interno del nativo verificato e del servizio, in ordine:
- la firma dell’asserzione (sempre obbligatoria; quella dell’involucro della risposta è opzionale a seconda della configurazione) — strutturalmente resistente al signature wrapping perché il payload convalidato è vincolato all’elemento verificato,
- l’issuer corrisponde all’IdP configurato,
- l’audience indica questo service provider,
- la destination è il nostro URL ACS,
InResponseTocorrisponde a una AuthnRequest in sospeso che abbiamo emesso (le risposte non richieste, avviate dall’IdP, sono disattivate per impostazione predefinita),- le finestre di validità rientrano nello scostamento dell’orologio (90s predefiniti) e
- l’id dell’asserzione non è mai stato visto prima (cache dei replay).
Gli errori tornano come codici tipizzati — signature, expired, audience, destination, issuer, request-match — e gli endpoint rispondono con errori generici, così un attaccante non scopre nulla sul motivo per cui un falso è fallito.
Account e pinning del subject
Sezione intitolata “Account e pinning del subject”Il primo accesso SSO fissa sull’account il subject immutabile del provider
(issuer più il NameID SAML / il sub OIDC, oppure l’externalId SCIM). Da
quel momento è il subject — non l’email — a identificare l’accesso. Se in
seguito qualcun altro presenta la stessa email dallo stesso IdP o da un
altro, l’account con il subject fissato lo rifiuta. È questo che impedisce
che un’email riciclata presso un provider si trasformi in un furto di
account.
Provisioning e deprovisioning
Sezione intitolata “Provisioning e deprovisioning”Il provisioning JIT avviene al momento dell’accesso (SsoProvisioner). SCIM
è una superficie push separata (ScimService) che fissa lo stesso subject
sulla riga dell’utente; non concede l’appartenenza agli spazi di lavoro. Il
deprovisioning — SCIM active: false o DELETE — rifiuta se l’account è
proprietario di uno spazio di lavoro (prima trasferisci la proprietà).
Altrimenti revoca prima ogni credenziale del dispositivo (le sessioni cadono
in pochi secondi tramite il watch del dispositivo), rimuove ogni appartenenza
agli spazi di lavoro e contrassegna l’account come disattivato. La riga
dell’utente sopravvive: l’attribuzione è permanente. La riattivazione
ripristina l’accesso ma non riconcede mai le appartenenze.
Cosa resta fuori
Sezione intitolata “Cosa resta fuori”- La mappatura gruppo→ruolo avviene all’accesso a partire dagli attributi SAML / dai claim OIDC — i gruppi inviati via SCIM non vengono memorizzati.
- Il ruolo di proprietario non è raggiungibile dall’SSO — né come ruolo predefinito né tramite una mappatura di gruppo.
- Il relay WebSocket non è mai un canale SSO; i telefoni che raggiungono il server solo attraverso il relay non possono completare un round-trip del browser verso di esso.