Ir al contenido

Inicio de sesión único

El inicio de sesión único permite que un proveedor de identidad (IdP) externo decida quién puede iniciar sesión en tu servidor de Control Center. Se admiten dos protocolos —SAML 2.0 y OpenID Connect— y ambos acaban en el mismo sitio: una credencial de dispositivo recién emitida que es indistinguible de una invitación o de un emparejamiento por QR en todo lo que viene después.

Los inicios de sesión SAML están mediados por el navegador. El IdP nunca abre una conexión con tu servidor; es el navegador del usuario el que lo transporta todo:

browser ──> /saml/login ──> IdP ──POST SAMLResponse──> /saml/acs
│ verify
▼
provision user (maybe)
│
▼
browser handoff: device PSK in the URL
fragment (never a query parameter)

Como el navegador es el mensajero, un servidor detrás de NAT funciona perfectamente para los inicios de sesión: localhost, la LAN, Tailscale, una VPN o un túnel sirven igual — solo el navegador del usuario debe alcanzar la URL de ACS. El retransmisor WebSocket que usa el control remoto desde el teléfono no puede transportar el ciclo HTTP de ida y vuelta, y por eso los puntos de entrada de SSO siempre apuntan al origen HTTP del servidor.

OpenID Connect sigue la misma forma con un ciclo de código de autorización + PKCE; las reclamaciones se leen del endpoint de tokens del emisor a través de TLS, de modo que el navegador nunca aporta datos de identidad.

Control Center reparte la responsabilidad de forma deliberada:

Pieza Dónde Se encarga de
cc_saml native packages/cc_natives/native/saml/ verificación XML-DSig, canonicalización y validación del perfil SAML — delegadas en el crate saml de Rust puro fijado tras una ABI de C sin estado
SamlService / OidcService cc_server_core el estado del inicio de sesión (rastreadores de solicitudes pendientes, deduplicación de repeticiones), los parámetros de la política de validación y la llamada de aprovisionamiento compartida
SsoProvisioner cc_server_core buscar o crear al usuario, fijar el sujeto del proveedor y conceder pertenencias según la política de miembro automático
ScimService cc_server_core la superficie de envío SCIM 2.0 (ciclo de vida de aprovisionamiento/desaprovisionamiento)
SsoSettingsService cc_server_core las filas de conexión guardadas, la reaplicación en vivo y el token SCIM

Deliberadamente no hay ninguna alternativa de criptografía SAML en Dart puro: la canonicalización/XML-DSig artesanal es justo donde viven las vulnerabilidades de envoltura de firma, así que la ausencia de la biblioteca cc_saml rechaza tanto el inicio de sesión SAML como el arranque del servidor. El componente nativo no tiene estado por diseño: el estado entre solicitudes vive en los servicios Dart, de un solo uso y de vida corta.

Dentro del nativo verificado y del servicio, en este orden:

  1. la firma de la aserción (siempre obligatoria; la del sobre de respuesta es opcional según la configuración), estructuralmente resistente a la envoltura de firma porque la carga útil validada está vinculada al elemento verificado,
  2. el emisor coincide con el IdP configurado,
  3. la audiencia nombra a este proveedor de servicio,
  4. el destino es nuestra URL de ACS,
  5. InResponseTo coincide con una AuthnRequest pendiente que emitimos (las respuestas no solicitadas iniciadas por el IdP están desactivadas de forma predeterminada),
  6. las ventanas de vigencia se mantienen dentro del desfase de reloj (90 s de forma predeterminada) y
  7. el id de aserción no se ha visto nunca antes (caché de repeticiones).

Los fallos se devuelven como códigos tipados —firma, caducado, audiencia, destino, emisor, coincidencia de solicitud— y los endpoints responden con errores genéricos, de modo que un atacante no aprende nada sobre por qué falló una falsificación.

El primer inicio de sesión SSO fija el sujeto inmutable del proveedor (issuer más el NameID de SAML / sub de OIDC, o el externalId de SCIM) en la cuenta. A partir de entonces, es el sujeto —no el correo electrónico— el que identifica el inicio de sesión. Si otra persona presenta más adelante el mismo correo desde el mismo IdP o desde otro, la cuenta fijada la rechaza. Esto es lo que impide que un correo reciclado en un proveedor se convierta en una toma de control de la cuenta.

El aprovisionamiento JIT ocurre al iniciar sesión (SsoProvisioner). SCIM es una superficie de envío aparte (ScimService) que fija el mismo sujeto en la fila del usuario; no concede pertenencias a espacios de trabajo. El desaprovisionamiento —active: false o DELETE de SCIM— se niega si la cuenta es propietaria de un espacio de trabajo (transfiere la propiedad primero). En caso contrario, revoca primero todas las credenciales de dispositivo (las sesiones caen en cuestión de segundos gracias a la vigilancia de dispositivos), elimina todas las pertenencias a espacios de trabajo y marca la cuenta como desactivada. La fila del usuario sobrevive: la atribución es permanente. La reactivación restaura el inicio de sesión, pero nunca vuelve a conceder pertenencias.

  • La asignación de grupos→roles ocurre al iniciar sesión a partir de los atributos SAML / las reclamaciones OIDC; los grupos SCIM enviados no se almacenan.
  • El rol de propietario es inalcanzable desde SSO, ni como rol predeterminado ni mediante una asignación de grupos.
  • El retransmisor WebSocket nunca es un canal de SSO; los teléfonos que solo llegan al servidor a través del retransmisor no pueden completar un ciclo de ida y vuelta por el navegador contra él.