> ## Documentation Index
> Fetch the complete documentation index at: https://docs.userkit.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Autenticação

> Qual credencial abre qual endpoint, e o que acontece quando você leva a errada.

Quatro famílias de credencial, quatro superfícies. Uma credencial de uma nunca abre
as rotas de outra — isso é garantido pelo prefixo, antes de qualquer consulta ao
banco.

| Família   | Header                                                                     | Abre                                                                                                            |
| --------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `uk_st_…` | `Authorization: Bearer`                                                    | O plano de staff: conta, organizações, membros, papéis, chaves, leituras de contato no painel                   |
| `uk_sk_…` | `Authorization: Bearer`                                                    | `/v1/me`, `/v1/contacts`                                                                                        |
| `uk_pk_…` | **corpo** da requisição, ou o **caminho** das leituras endereçadas por ela | `/v1/boot`, `/v1/contact-auth/*`, `/v1/waitlist`, `/v1/jwks/{publishableKey}`, `/v1/config/{publishableKey}`    |
| `uk_ct_…` | `Authorization: Bearer`                                                    | `/v1/contact/me`, `/v1/contact/logout`, `/v1/contact/token`, `/v1/contact/sessions`, `/v1/contact/two-factor/*` |

Detalhes completos em [Credenciais](/pt-br/concepts/credentials).

## Sessões de staff

Consiga uma no cadastro, no login, ou na troca de dois fatores:

```bash theme={null}
curl -s $API/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{ "email": "ada@example.com", "password": "Analytical1" }'
```

```json theme={null}
{
  "token": "uk_st_9f3c…",
  "expires_at": "2026-08-28T12:00:00Z",
  "user": { "...": "..." },
  "organizations": [{ "...": "..." }],
  "active_organization_id": "…",
  "active_organization_code": "org_4b1e…"
}
```

Contas com dois fatores respondem isto no lugar, e nenhuma sessão é emitida ainda:

```json theme={null}
{ "two_factor_required": true, "challenge_token": "uk_2fa_…" }
```

Ramifique em `two_factor_required`, depois troque o desafio em
`/v1/auth/two-factor` dentro de 5 minutos. Veja
[Dois fatores](/pt-br/guides/two-factor#entrando-com-ela).

Depois envie em toda requisição:

```bash theme={null}
curl -s $API/v1/session \
  -H "Authorization: Bearer uk_st_9f3c…" \
  -H "X-Organization-Id: org_4b1e…"
```

Sessões vivem 30 dias e são revogáveis: sair, trocar a senha, redefinir a senha e
perder o vínculo matam todas elas no servidor.

## Chaves de API

Só no servidor. Criadas no painel, mostradas uma vez.

```bash theme={null}
curl -s $API/v1/me -H "Authorization: Bearer uk_sk_live_a91c…"
```

O ambiente da chave é resolvido a partir da linha armazenada em toda requisição.
Chame `/v1/me` uma vez no start-up para confirmar qual você está segurando.

## Chaves publicáveis

Não é header. Vai no corpo dos endpoints que a aceitam:

```bash theme={null}
curl -s $API/v1/boot \
  -H 'Origin: https://app.example.com' \
  -H 'Content-Type: application/json' \
  -d '{ "publishable_key": "uk_pk_live_3d8e…", "anonymous_id": "anon_2f9c1b" }'
```

O portão não é o CORS — esses caminhos respondem a qualquer origem,
deliberadamente. O portão é a
[lista de origens permitidas](/pt-br/guides/api-keys#a-lista-de-origens-permitidas)
da chave. Vazia significa qualquer origem; preenchida significa só correspondências
exatas, e um header `Origin` ausente é recusado.

## Sessões de contato

Emitidas pelo boot, por um magic link resgatado, por login hosted, ou por
verificação de e-mail. Leem aquele contato e mais nada.

```bash theme={null}
curl -s $API/v1/contact/me -H "Authorization: Bearer uk_ct_…"
```

O `verified` na sessão diz se a identidade por trás dela foi **comprovada**. Uma
sessão não verificada é usável e fica marcada, mas é barrada de qualquer coisa por
onde os dados de outra pessoa pudessem vazar.

## Falhas

<ResponseField name="401 unauthorized">
  Credencial ausente, malformada, expirada ou revogada — e também "você não é membro
  daquela organização", porque o JOIN do vínculo não casa com nada e distinguir os
  dois casos vazaria que a organização existe.
</ResponseField>

<ResponseField name="403 forbidden">
  Autenticado, mas o seu papel não carrega a permissão. Diferente do `401`: logar de
  novo não vai ajudar.
</ResponseField>

<ResponseField name="404 not_found">
  Também a resposta para um recurso em outra organização ou outro ambiente. O 404
  nunca revela qual.
</ResponseField>

### Levando a família errada

Uma credencial bem formada de outra família recebe isso, em vez de um "inválido"
seco:

```json 401 theme={null}
{
  "error": {
    "code": "unauthorized",
    "message": "this endpoint expects a staff session token, not an organization API key"
  }
}
```

Um caso é erro de configuração, o outro é evento de segurança. Eles não deveriam ler
igual nos seus logs.

## Não revelar se uma conta existe

Vários endpoints respondem igual havendo ou não conta para aquele endereço. Isso é
deliberado e vale preservar se você mexer nesses caminhos:

| Endpoint                                    | Comportamento                                                                                                                       |
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `POST /v1/auth/login`                       | Um `401` para e-mail desconhecido, conta sem senha e senha errada — com uma comparação bcrypt descartável para o tempo bater também |
| `POST /v1/auth/forgot-password`             | `202` sempre                                                                                                                        |
| `POST /v1/contact-auth/signup`              | `202` sempre — três coisas diferentes acontecem por trás                                                                            |
| `POST /v1/contact-auth/login`               | Um `401`, mesma equalização de tempo                                                                                                |
| `POST /v1/contact-auth/forgot-password`     | `202` sempre                                                                                                                        |
| `POST /v1/contact-auth/resend-verification` | `202` sempre                                                                                                                        |
| `POST /v1/contact-auth/magic-link`          | `202` sempre                                                                                                                        |
| `POST /v1/contact-auth/magic-link/redeem`   | Um `401` para usado, expirado e nunca existiu                                                                                       |
| `POST /v1/contact-auth/email-code`          | `202` sempre — o desafio é gerado antes de qualquer consulta, então um endereço sem conta também recebe um                          |
| `POST /v1/contact-auth/email-code/verify`   | Um `401` para errado, expirado, já gasto, e para um desafio que nunca teve código atrás                                             |

Sem a equalização de tempo, só contas reais pagariam as dezenas de milissegundos do
bcrypt, e a latência sozinha responderia a pergunta que o código de status se recusa
a responder.
