# Login único (SSO)

> Use um provedor de identidade confiável no login do Nodaro self-hosted: configure EXTERNAL_SSO_PROVIDERS, a vinculação de contas e o login só por SSO.

Source: https://nodaro.ai/pt-BR/docs/self-hosting/sso

O **login único (SSO)** permite que um provedor de identidade externo e confiável faça o login de usuários no seu Nodaro self-hosted. Você descreve cada provedor na variável `EXTERNAL_SSO_PROVIDERS`, e a página de login mostra um botão de SSO. A conta em que o usuário entra é um usuário comum da sua instalação: o SSO é só uma forma de iniciar uma sessão. Esta página trata do lado do operador; o protocolo está em [Login externo (SSO) para desenvolvedores](https://nodaro.ai/docs/developers/sso).

## Que tipo de provedor usar
| Tipo | Use quando | Status |
| --- | --- | --- |
| `assertion` | O seu emissor não fala OpenID Connect nem SAML, mas consegue assinar um JWT de curta duração, por exemplo um app que incorpora o Nodaro. O Nodaro verifica o token e o troca por uma sessão comum. | Funciona de ponta a ponta. |
| `oidc` | O seu provedor de identidade fala OpenID Connect e está configurado no Supabase Auth da sua instalação. O navegador faz um redirecionamento comum de login do Supabase, e o Supabase verifica o resultado. | O `id` do provedor precisa ser o nome do provedor OAuth no seu Supabase Auth, como `keycloak` ou `azure`. |
| `saml` | — | As entradas passam na validação, mas a página de login não consegue enviar um domínio SAML, então elas não fazem o login de ninguém. Use `assertion` ou `oidc`. |

O SSO vem desativado por padrão. Com `EXTERNAL_SSO_PROVIDERS` sem definir, a página de login não mostra nenhum botão de SSO, `GET /v1/sso/providers` retorna uma lista vazia, e todas as rotas de provedor respondem `404 unknown_provider`.

## Configurar os provedores
Defina `EXTERNAL_SSO_PROVIDERS` como um array JSON, direto no valor ou como `@/path/to/providers.json` para ler um arquivo.

```json
[
{
"id": "librechat",
"label": "LibreChat",
"kind": "assertion",
"secret": "a-dedicated-32+char-hmac-key-not-the-idp-session-secret",
"audience": "nodaro",
"claimMap": { "email": "email", "emailVerified": "email_verified", "subject": "sub" },
"initiateUrl": "https://chat.example.com/oauth/nodaro",
"maxLifetimeSeconds": 300
},
{
"id": "keycloak",
"label": "Acme (Keycloak)",
"kind": "oidc",
"supabaseProvider": "keycloak"
}
]
```

Um valor malformado **interrompe a inicialização**. Um erro de digitação em uma entrada que guarda um segredo nunca pode descartar um provedor em silêncio, o que pareceria uma falha do SSO com `404`, nem deixar o login configurado pela metade.

Na stack do Compose, adicione `EXTERNAL_SSO_PROVIDERS` e `EXTERNAL_SSO_LINK_EXISTING` em `environment:` do serviço `nodaro`, porque o arquivo do Compose não as repassa do `.env`. Para usar um arquivo, monte-o no contêiner e informe o caminho dele dentro do contêiner.

### Campos do provedor
| Campo | Aplica-se a | O que faz |
| --- | --- | --- |
| `id` | Todos | Um nome seguro para URLs: primeiro uma letra minúscula ou um dígito, depois letras minúsculas, dígitos, `_` ou `-`, de 1 a 63 caracteres no total, sem pontos. Ele aparece na rota `/v1/sso/<id>` e é guardado no usuário. Precisa ser único. |
| `label` | Todos | O nome do provedor. Ele só aparece no botão de login quando existem dois ou mais provedores e o perfil de superfície não define `auth.ssoLabel`. |
| `kind` | Todos | `assertion`, `oidc` ou `saml`. |
| `secret` | `assertion`, obrigatório | A chave HS256 que verifica a assinatura da asserção, com pelo menos 16 caracteres. Use uma chave dedicada, nunca o segredo de sessão do próprio provedor de identidade, para que um vazamento do lado do Nodaro não permita forjar as sessões do provedor. |
| `audience` | `assertion`, obrigatório | O valor a que a claim `aud` da asserção precisa ser igual. |
| `claimMap` | `assertion` | Quais claims trazem o e-mail, o indicador de verificação e o subject. O padrão é `{ "email": "email", "emailVerified": "email_verified", "subject": "sub" }`. |
| `initiateUrl` | `assertion` | Uma URL absoluta. Quando um usuário clica no botão de login, o Nodaro redireciona para ela. O provedor faz o login do usuário e volta com a asserção. Sem ela, um clique responde `400 no_assertion`. |
| `initiateUrlByHost` | `assertion` | Um mapa de um nome de host simples, em minúsculas e sem porta, para uma URL absoluta. Um clique que chega por esse host vai para essa URL em vez de `initiateUrl`. Para instalações que respondem em vários nomes de host. Uma chave errada interrompe a inicialização, e o mapa nunca é publicado. |
| `maxLifetimeSeconds` | `assertion` | O maior tempo de vida que uma asserção pode ter, `exp` menos `iat`. O padrão é `300`, ou seja, 5 minutos, e o máximo é `3600`. |
| `supabaseProvider` | `oidc` | Validado, mas não usado: a página de login passa ao Supabase o `id` do provedor. |
| `domain` | `oidc`, `saml` | Validado, mas não usado. Uma entrada `oidc` ou `saml` precisa de `domain` ou de `supabaseProvider`. |

Só `id`, `label` e `kind` são publicados, por `GET /v1/sso/providers`. O segredo nunca sai do servidor.

## O que o Nodaro verifica em uma asserção
O servidor de identidade de um provedor `assertion` assina um JWT e envia o navegador para `GET /v1/sso/<id>?assertion=<jwt>`. O Nodaro só aceita a asserção quando todas estas condições são cumpridas:

- Ela é assinada com **HS256** e com o `secret` do provedor.
- **`aud`** é igual ao `audience` do provedor.
- **`exp`** está presente e não está no passado, com 5 segundos de tolerância, e o tempo de vida está dentro de `maxLifetimeSeconds`.
- **`jti`** está presente e nunca foi usado. Cada asserção funciona uma vez.
- A claim de **e-mail** está presente. A claim de e-mail **verificado** precisa ser `true` para criar ou vincular uma conta.

Uma verificação que falha responde `401`. A rota aceita 20 requisições a cada 60 segundos de um mesmo endereço IP e, acima disso, responde `429`. O contrato completo, os códigos de erro e o fluxo de redirecionamento estão em [Login externo (SSO) para desenvolvedores](https://nodaro.ai/docs/developers/sso).

## Como as contas são vinculadas
Quando uma asserção passa, o Nodaro encontra ou cria o usuário correspondente. As regras garantem que uma asserção nunca possa assumir uma conta existente só porque ela tem o mesmo endereço de e-mail.

| Situação | Resultado |
| --- | --- |
| Nenhuma conta tem o e-mail, e o e-mail está verificado | Um usuário novo é criado e vinculado ao provedor. |
| Nenhuma conta tem o e-mail, e ele não está verificado | Recusado: `403 email_unverified`. |
| A conta já está vinculada a este provedor | O usuário entra. |
| A conta está vinculada a outro provedor | Recusado: `403 account_linked_other_provider`, mesmo com `EXTERNAL_SSO_LINK_EXISTING=true`. |
| Existe uma conta local sem SSO, `EXTERNAL_SSO_LINK_EXISTING=true`, e o e-mail está verificado | A conta é vinculada ao provedor, e o usuário entra. |
| Existe uma conta local sem SSO, e a opção é `false` ou o e-mail não está verificado | Recusado: `403 account_exists`. |

O padrão de `EXTERNAL_SSO_LINK_EXISTING` é `false`, o que protege contra a tomada de contas. Só `true` ou `1`, com qualquer combinação de maiúsculas e minúsculas, ativam a vinculação. Ative-a só quando você confiar nas claims de e-mail verificado dos seus provedores a ponto de associá-las a contas existentes.

`account_exists` e `email_unverified` usam o mesmo texto para todas as contas, então o formulário de login não pode ser usado para descobrir quais endereços existem. O log do servidor registra qual conta foi o alvo.

## O botão de login
A página de login mostra um botão de SSO quando pelo menos um provedor está configurado.

- **Um provedor:** o botão mostra **Entrar com SSO**, ou o `auth.ssoLabel` do perfil de superfície.
- **Vários provedores:** cada botão mostra o `label` do provedor dele, a menos que o perfil de superfície defina `auth.ssoLabel`.

Depois do login, o usuário chega a `/projects`. O provedor pode enviar outra página adicionando `&next=<path>` ao redirecionar de volta. O Nodaro só respeita esse valor quando ele é um caminho relativo na mesma origem, começando com `/`, mas não com `//`. Qualquer outro valor leva a `/projects`. Nos hosts listados em `CORS_ORIGIN`, o redirecionamento para a página de destino continua relativo, no host pelo qual o usuário chegou.

## Permitir o login só por SSO
Na Business edition, o perfil de superfície pode remover os outros métodos de login:

```bash
NODARO_SURFACE_PROFILE={"auth":{"methods":["sso"],"ssoLabel":"Sign in with Acme"}}
```

Uma lista que cita só `sso` também ativa uma regra no servidor: toda conta logada precisa ter sido criada ou vinculada pelo SSO. Qualquer outra sessão é recusada na primeira chamada à API com `403 sso_required`, então uma conta registrada diretamente no serviço de login não pode usar a instalação. Adicionar `email` à lista desativa a regra. Veja [Edições e perfis de superfície](https://nodaro.ai/docs/self-hosting/editions-and-profiles#sign-in-methods).

## Resumo de segurança
- **Um segredo dedicado** por provedor, diferente do segredo de sessão do próprio provedor.
- **Asserções de uso único**, com um tempo de vida curto, imposto pelo servidor.
- **Só redirecionamentos na mesma origem** depois do login.
- **Nenhum segredo sai do servidor.** As asserções e os tokens de uso único são removidos dos logs de requisições.
- **Limites de taxa** na rota de troca, por endereço IP.
- **Sessões comuns.** Depois da troca, o usuário é um usuário normal, sem nenhuma credencial especial.

## Frequently asked questions

### O login único vem ativado por padrão?

Não. Com EXTERNAL_SSO_PROVIDERS sem definir, a página de login não mostra nenhum botão de SSO, e todas as rotas de SSO respondem 404. Configure pelo menos um provedor para ativá-lo.

### Que tipo de provedor devo configurar?

Use assertion para um emissor que consegue gerar um token assinado de curta duração, como um app que incorpora o Nodaro. Use oidc para um provedor de identidade que você configurou no Supabase Auth da sua instalação. As entradas do tipo saml passam na validação, mas não fazem o login de ninguém.

### O SSO pode assumir uma conta existente com o mesmo e-mail?

Não por padrão. EXTERNAL_SSO_LINK_EXISTING é false, então uma asserção para um e-mail que já tem uma conta local é recusada com account_exists. Defina essa variável como true só quando você confiar nas claims de e-mail verificado dos seus provedores.

### Como permito o login só por SSO?

Na Business edition, defina o auth.methods do perfil de superfície como ["sso"], com um auth.ssoLabel. Toda conta precisa, então, vir pelo SSO, e qualquer outra sessão é recusada com 403 sso_required.

### O que acontece se EXTERNAL_SSO_PROVIDERS tiver um erro?

A instalação se recusa a iniciar. Um valor malformado nunca descarta um provedor em silêncio nem deixa o login configurado pela metade.
