Pular para o conteúdo

Logon único

O logon único permite que um provedor de identidade (IdP) externo decida quem pode entrar no seu servidor do Control Center. Dois protocolos são suportados — SAML 2.0 e OpenID Connect — e ambos chegam ao mesmo lugar: uma credencial de dispositivo recém-emitida, indistinguível de um convite ou de um emparelhamento por QR em todo o restante do caminho.

Os logins SAML são mediados pelo navegador. O IdP nunca abre uma conexão com o seu servidor; é o navegador do usuário que carrega tudo:

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 o navegador é o mensageiro, um servidor atrás de NAT funciona sem problemas para logins: localhost, LAN, Tailscale, VPN ou um túnel servem — apenas o navegador do usuário precisa alcançar a URL do ACS. O relay WebSocket usado pelo controle remoto por celular não consegue transportar o ida e volta HTTP, e é por isso que os pontos de entrada de SSO sempre miram a origem HTTP do servidor.

O OpenID Connect segue o mesmo formato, com um ida e volta de código de autorização + PKCE; as declarações são lidas do endpoint de token do emissor por TLS, então o navegador nunca fornece dados de identidade.

O Control Center divide a responsabilidade de propósito:

Componente Onde Responsabilidade
cc_saml nativo packages/cc_natives/native/saml/ Verificação de XML-DSig, canonicalização, validação de perfil SAML — delegadas ao crate saml de Rust puro fixado por versão, atrás de uma ABI C sem estado
SamlService / OidcService cc_server_core estado de login (rastreadores de requisições pendentes, deduplicação de replay), ajustes de política de validação, a chamada de provisionamento compartilhada
SsoProvisioner cc_server_core encontrar ou criar o usuário, fixar o sujeito do provedor, conceder associações sob a política de membro automático
ScimService cc_server_core a superfície de push do SCIM 2.0 (ciclo de vida de provisionamento/desprovisionamento)
SsoSettingsService cc_server_core as linhas de conexão salvas, a reaplicação em tempo real, o token do SCIM

Não existe, de propósito, nenhum fallback de criptografia SAML em Dart puro: XML-DSig/canonicalização feitos à mão são exatamente onde vivem as vulnerabilidades de encapsulamento de assinatura, então a ausência da biblioteca cc_saml recusa tanto o login SAML quanto a inicialização do servidor. O nativo é sem estado por projeto — o estado entre requisições vive nos serviços Dart, de uso único e curta duração.

Dentro do nativo verificado e do serviço, nesta ordem:

  1. a assinatura da asserção (sempre obrigatória; a do envelope de resposta é opcional conforme a configuração) — estruturalmente resistente a encapsulamento de assinatura, porque o payload validado está vinculado ao elemento verificado,
  2. o emissor corresponde ao IdP configurado,
  3. o audience nomeia este provedor de serviço,
  4. o destino é a nossa URL do ACS,
  5. InResponseTo corresponde a um AuthnRequest pendente que nós emitimos (respostas não solicitadas, iniciadas pelo IdP, estão desativadas por padrão),
  6. as janelas de tempo de vida se mantêm dentro do desvio de relógio (padrão de 90s) e
  7. o id da asserção nunca foi visto antes (cache de replay).

As falhas voltam como códigos tipados — signature, expired, audience, destination, issuer, request-match — e os endpoints respondem com erros genéricos, para que um atacante não descubra por que uma falsificação falhou.

O primeiro login por SSO fixa o sujeito imutável do provedor (issuer mais o NameID do SAML / o sub do OIDC, ou o externalId do SCIM) na conta. A partir daí, o sujeito — não o e-mail — identifica o login. Se outra pessoa mais tarde apresentar o mesmo e-mail vindo do mesmo IdP ou de outro, a conta fixada a recusa. É isso que impede que um e-mail reciclado em um provedor se torne uma tomada de conta.

O provisionamento JIT acontece no login (SsoProvisioner). O SCIM é uma superfície de push separada (ScimService) que fixa o mesmo sujeito na linha do usuário; ele não concede associações a espaços de trabalho. O desprovisionamento — SCIM active: false ou DELETE — recusa se a conta for proprietária de um espaço de trabalho (transfira primeiro). Caso contrário, ele revoga primeiro todas as credenciais de dispositivo (as sessões caem em segundos pelo monitoramento de dispositivos), remove todas as associações a espaços de trabalho e marca a conta como desativada. A linha do usuário sobrevive: a atribuição é permanente. A reativação restaura o login, mas nunca volta a conceder associações.

  • O mapeamento de grupo → papel acontece no login, a partir dos atributos SAML / das declarações OIDC — grupos enviados por push via SCIM não são armazenados.
  • O papel de proprietário é inalcançável a partir do SSO — nem como papel padrão nem por meio de um mapeamento de grupo.
  • O relay WebSocket nunca é um canal de SSO; celulares que alcançam o servidor apenas pelo relay não conseguem completar um ida e volta pelo navegador contra ele.