> ## 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.

# Verificação em duas etapas

> TOTP para contas de staff, com códigos de recuperação e uma configuração que não tranca ninguém para fora.

A verificação em duas etapas vale para **contas de staff** — as pessoas que entram
no painel. É TOTP: um app autenticador, um código de 6 dígitos, mais 8 códigos de
recuperação de uso único.

<Note>
  Quando o servidor não pode guardar um segredo TOTP com segurança, toda rota aqui
  responde `501 two_factor_unavailable` e `GET /v1/session` informa
  `two_factor_available: false` — leia essa flag antes de mostrar a seção, em vez de
  esbarrar no `501` no meio do fluxo.
</Note>

## Ligando

Duas chamadas, e a ordem é o ponto.

<Steps>
  <Step title="Inicie a configuração">
    ```bash theme={null}
    curl -s -X POST $API/v1/account/two-factor/setup \
      -H "Authorization: Bearer $UK_SESSION"
    ```

    ```json theme={null}
    {
      "secret": "JBSWY3DPEHPK3PXP",
      "otpauth_url": "otpauth://totp/Userkit:ada@example.com?secret=…",
      "qr_data_uri": "data:image/png;base64,…"
    }
    ```

    Renderize `qr_data_uri` para escanear e mostre `secret` para quem não consegue
    escanear. Isso **não liga a verificação em duas etapas** — apenas guarda o segredo,
    cifrado.
  </Step>

  <Step title="Ative com um código">
    ```bash theme={null}
    curl -s -X POST $API/v1/account/two-factor/enable \
      -H "Authorization: Bearer $UK_SESSION" \
      -H 'Content-Type: application/json' \
      -d '{ "code": "123456" }'
    ```

    ```json theme={null}
    {
      "totp_enabled": true,
      "recovery_codes": ["a1b2c-3d4e5-f6a7b-8c9d0", "e5f6a-7b8c9-d0e1f-2a3b4", "…"]
    }
    ```
  </Step>
</Steps>

Dividir em dois é o que impede uma configuração abandonada de trancar alguém para
fora: o segredo existe, mas os dois fatores ficam desligados até o usuário provar
que consegue produzir um código a partir dele.

<Warning>
  `recovery_codes` aparece uma vez e só aqui. Só os hashes são armazenados. Peça ao
  usuário para guardá-los em algum lugar que não seja o celular com o autenticador.
</Warning>

Chamar setup com os dois fatores já ligados responde `409 already_enabled`. Chamar
enable antes do setup responde `409 setup_required`.

## Entrando com ela

O login vira duas etapas.

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

    ```json 200 — ainda sem sessão theme={null}
    {
      "two_factor_required": true,
      "challenge_token": "uk_2fa_…"
    }
    ```

    Ramifique em `two_factor_required`. O desafio vale **5 minutos**, e não é uma sessão
    — ele carrega o próprio prefixo justamente para nunca poder ser usado como uma.
  </Step>

  <Step title="Código">
    ```bash theme={null}
    curl -s $API/v1/auth/two-factor \
      -H 'Content-Type: application/json' \
      -d '{ "challenge_token": "uk_2fa_…", "code": "123456" }'
    ```

    Responde uma sessão normal — o mesmo corpo de um login sem dois fatores.
  </Step>
</Steps>

Um código errado responde `401 invalid_code` e **não consome o desafio**: errar um
código de 6 dígitos é o caso comum. Ele gasta uma das dez tentativas do desafio, e
um desafio que acaba as tentativas responde `invalid_challenge` daí em diante. O
rate limit conta por endereço e quem ataca distribui os chutes entre endereços,
então é o orçamento do próprio desafio que de fato limita adivinhar. Um código
correto consome o desafio de uma vez; ele morre com o login que autorizou.

## Códigos de recuperação

Tanto um código TOTP quanto um de recuperação satisfazem o campo `code`. Um código
de recuperação é consumido no uso, e usar um envia um aviso para o e-mail da conta —
esse aviso é como alguém descobre que um código que nunca usou foi usado.

Regerar substitui o conjunto inteiro; os antigos param de funcionar:

```bash theme={null}
curl -s -X POST $API/v1/account/two-factor/recovery-codes \
  -H "Authorization: Bearer $UK_SESSION" \
  -H 'Content-Type: application/json' \
  -d '{ "password": "Analytical1" }'
```

Nunca parcial. Um conjunto parcialmente conhecido é pior que um novo.

## Desligando

```bash theme={null}
curl -s -X POST $API/v1/account/two-factor/disable \
  -H "Authorization: Bearer $UK_SESSION" \
  -H 'Content-Type: application/json' \
  -d '{ "password": "Analytical1" }'
```

A senha atual é obrigatória. Uma sessão roubada não pode bastar para arrancar o
segundo fator de uma conta — é justamente para isso que ele existe. Senha errada
responde `401 invalid_credentials`: a sessão está boa, quem não confere é a
credencial recém-digitada.

Desligar deleta os códigos de recuperação junto com o segredo, e avisa a conta por
e-mail.

## Notificações

Quatro momentos disparam um e-mail, e cada um existe para que uma ação que o dono
não tomou fique visível:

* dois fatores ativados;
* dois fatores desativados;
* um código de recuperação usado para entrar;
* a senha alterada ou redefinida.

## O que isso não cobre

Os dois fatores são para contas de staff. **Contatos** — os seus usuários — não têm.
A superfície de segurança deles é a
[autenticação hosted](/pt-br/customer-auth/hosted) ou a
[identidade federada](/pt-br/customer-auth/federated), onde o segundo fator, se você
quiser um, é seu para operar.
