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

# Dois fatores

> TOTP para os seus clientes, exigido em toda porta de entrada — inclusive no botão social.

Dois fatores para contatos é a mesma máquina que o painel dá para a sua equipe,
uma camada abaixo: aplicativo autenticador, códigos de recuperação de uso único
e um desafio curto entre as duas etapas.

<Note>
  É de graça, e sempre será. Nenhum método de autenticação fica atrás de plano
  pago aqui — cobrar pelo segundo fator transforma o tier gratuito numa armadilha
  e segurança em upsell.
</Note>

## A decisão que vale conhecer

**Ele é exigido em todo lugar que emite sessão.** Login por senha, magic link,
código por e-mail, o link de confirmação e login social param todos no mesmo
desafio.

Um segundo fator que se contorna clicando em "continuar com o Google" é um
checkbox, não um controle — e quem ligou não tem como saber quais portas ele
cobre de verdade. Então ele cobre todas.

<Warning>
  Redefinir a senha **não** desliga. Deixar desligar entregaria o desvio para
  quem tem a caixa de entrada, que é a maior parte do que dois fatores existe
  para impedir.

  A consequência é real e você deve se planejar para ela: um contato que perde o
  celular **e** os códigos de recuperação não volta sozinho. Ainda não existe
  override do lado do tenant — veja [o que falta](#o-que-falta).
</Warning>

## Cadastrando

Duas chamadas, a partir de uma sessão de contato.

<Steps>
  <Step title="Setup">
    ```bash theme={null}
    curl -s -X POST $API/v1/contact/two-factor/setup \
      -H "Authorization: Bearer uk_ct_…"
    ```

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

    Renderize o QR; mostre o segredo para quem não consegue escanear. Isso **não**
    liga o segundo fator.
  </Step>

  <Step title="Enable">
    ```bash theme={null}
    curl -s -X POST $API/v1/contact/two-factor/enable \
      -H "Authorization: Bearer uk_ct_…" \
      -H 'Content-Type: application/json' \
      -d '{ "code": "123456" }'
    ```

    ```json 200 theme={null}
    { "totp_enabled": true, "recovery_codes": ["a1b2c-3d4e5-f6a7b-8c9d0", "…"] }
    ```

    Só agora está ligado. Esperar por um código que funciona faz com que um setup
    abandonado — a aba fechada no meio, o app errado escaneado — nunca tranque
    ninguém do lado de fora.
  </Step>
</Steps>

Os códigos de recuperação são mostrados **uma vez**. Mostre uma vez também, e
diga para que servem.

## Entrando

Por qualquer porta que tenha chegado, um contato com dois fatores recebe um
desafio em vez de uma sessão:

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

Esse token não é uma sessão — ele tem prefixo próprio justamente para nunca
poder ser gasto como uma — e vive dez minutos.

```bash theme={null}
curl -s $API/v1/contact-auth/two-factor \
  -H 'Content-Type: application/json' \
  -d '{
    "publishable_key": "uk_pk_live_…",
    "challenge_token": "uk_c2_…",
    "code": "123456"
  }'
```

A resposta é a resposta normal de login: contato, token `uk_ct_…`, expiração e
`verified`. Esse último campo carrega o que o *primeiro* fator conquistou — um
magic link e um callback social provam a identidade antes do código ser pedido,
e essa prova viaja no desafio em vez de ser recalculada depois.

Um código de recuperação entra no mesmo campo `code`. Ele é gasto ao ser usado,
e um aviso vai para a caixa de entrada — alguém usando código reserva merece
ser notado.

Um código errado **não** consome o desafio; errar seis 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, venha o código que vier
— o rate limit conta por endereço, e quem ataca distribui os chutes entre
endereços, então é o orçamento que de fato limita adivinhar um número de seis
dígitos. Dez está bem acima do que uma pessoa erra digitando; quem chega lá
começa um novo login.

## Desligando

```bash theme={null}
curl -s -X POST $API/v1/contact/two-factor/disable \
  -H "Authorization: Bearer uk_ct_…" \
  -H 'Content-Type: application/json' \
  -d '{ "code": "123456" }'
```

Um código que funciona agora, TOTP ou de recuperação. Não a senha: um contato
só-social não tem nenhuma, e a prova certa para remover o segundo fator é o
segundo fator.

O `POST /v1/contact/two-factor/recovery-codes` troca o conjunto inteiro do mesmo
jeito. Todo código anterior para de funcionar — um conjunto parcialmente
conhecido é pior que um novo.

## Só em modo hosted

Cadastrar num ambiente **federado** responde `409 environment_not_hosted`. Lá
quem autentica é o seu servidor e o `boot` afirma o resultado: não existe
momento que pudéssemos interromper para pedir um código. Dois fatores é nosso
para exigir só onde o login é nosso para executar — se o seu produto tem MFA
próprio, é lá que ele mora.

## O que falta

Não existe um jeito de **você** tirar o segundo fator de um contato que perdeu o
celular e os códigos de recuperação. Isso é deliberado, não esquecido: a
alternativa em cima da mesa era deixar a redefinição de senha desligar, o que
entrega o desvio para quem tem a caixa de entrada.

Isso pertence à superfície de suporte, auditado — não à autenticação. Até
chegar, o honesto a dizer para os seus clientes é: guardem os códigos de
recuperação.
