Usar este modelo
Exemplo técnico fictício, não arquitetura real da bLOw. Serviços, endpoints, modelo de dados, limites, pessoas e decisões abaixo são hipóteses didáticas. Valide tudo com o código, infraestrutura, segurança e equipe reais antes de implementar.
RFC-001 · Especificação técnica para engenharia sênior

Reenvio seguro do código de verificação do cadastro

Desenho proposto para permitir um novo código quando o anterior expirar, sem gerar dois envios em pedidos simultâneos e sem expor dados sensíveis.

SituaçãoProposta · revisão pendente
Responsável técnicoBruno Costa (exemplo)
RelacionadosPRJ-001 · FEAT-001 · FIX-001
Última revisão22 de setembro de 2026

1. Decisão em uma página

Adotar um único registro ativo de verificação por tentativa de cadastro, com geração atômica de um novo código, invalidação da geração anterior e gravação durável de um evento de envio na mesma transação. Um trabalhador assíncrono envia o e-mail com retentativas controladas. A API aplica idempotência e limites no servidor; desabilitar o botão no cliente é apenas uma proteção adicional.

Limite de segurança: este código confirma controle do endereço de e-mail durante o cadastro. Não é autenticação multifator nem deve ser reutilizado como segundo fator de login. E-mail não é um canal de autenticação fora de banda aceito pelo NIST SP 800-63B.

2. Contexto, escopo e premissas

A FEAT-001 precisa recuperar um cadastro bloqueado por código expirado; o FIX-001 mostra o risco de dois envios por duplo toque. Esta RFC cobre o contrato de reenvio, persistência, concorrência, segurança, telemetria e implantação. Não muda provedor de e-mail, autenticação após cadastro nem a interface inteira.

Premissa do exemploValidação obrigatória na bLOw
Existe uma API de cadastro e uma tentativa identificável no servidor.Localizar o fluxo e confirmar quem é dono do estado da verificação.
Há banco transacional e envio de e-mail assíncrono.Confirmar banco, fila, provedor, garantias de entrega e observabilidade disponíveis.
O cadastro permanece pendente até o e-mail ser confirmado.Confirmar estados atuais da conta e efeitos de um código expirado.

3. Fluxo proposto e invariantes

Cliente → API de cadastro → serviço de verificação → banco + outbox → worker → provedor de e-mail
O cliente nunca gera nem valida o código. A API decide se o pedido pode prosseguir; o worker apenas entrega a geração autorizada.
  1. O cliente envia a tentativa de cadastro e uma chave de idempotência. O servidor identifica a tentativa pela sessão autenticada para esse fluxo; não confia em um e-mail arbitrário do corpo.
  2. Em transação com bloqueio da tentativa, o serviço verifica estado, janela de reenvio e cotas. Se permitido, cria uma nova geração, invalida a anterior e persiste um evento na outbox.
  3. A API retorna aceite. O worker consome o evento, envia somente a geração ainda válida e registra entrega ou falha. Entrega do provedor não equivale a leitura pelo usuário.
  4. Na confirmação, o servidor compara o código recebido com o digest da geração ativa, dentro do prazo e do limite de tentativas, e consome o desafio de forma atômica.

Invariantes: no máximo uma geração ativa por tentativa; código de uso único; código antigo nunca volta a ser válido; replays da mesma chave não geram novo e-mail; nenhuma conta passa a “verificada” antes da confirmação válida.

4. Contrato da API — proposta

POST /v1/signup/verification/resend com sessão de cadastro válida e cabeçalho Idempotency-Key gerado pelo cliente. O vínculo da tentativa vem da sessão; o cliente não escolhe o destinatário. Nomes e versionamento são ilustrativos e devem seguir as convenções reais da API.

POST /v1/signup/verification/resend
Idempotency-Key: 6b5b6e81-...  (exemplo)

202 Accepted
{"status":"queued","retry_after_seconds":60}

429 Too Many Requests
{"code":"RESEND_LIMITED","retry_after_seconds":45}

Repetir a mesma chave dentro da janela de retenção devolve o mesmo resultado lógico, sem outra geração nem outro envio. Uma chave nova durante o intervalo é limitada. 503 indica indisponibilidade temporária antes de persistir o pedido. Para tentativas de cadastro não reconhecidas, definir resposta indistinguível quanto à existência de conta, sem informar se o e-mail está cadastrado. O cliente deve tratar 202 como “pedido recebido”, não como “e-mail entregue”.

Compatibilidade: o endpoint existente de confirmação deve aceitar apenas a geração ativa. Se a arquitetura real já tiver contrato público, preferir evolução compatível a criar rota paralela; documentar mudanças e consumidores antes da implementação.

5. Dados e consistência

Campo propostoUsoCuidado
challenge_id, subject_id, email_refVincular código à tentativa, pessoa e destino já conhecidos pelo servidor.Referências internas; não registrar e-mail completo na telemetria.
generation, code_digest, expires_at, consumed_atIdentificar apenas o código vigente, expiração e uso único.Nunca armazenar o código em claro; revisar o esquema de digest com Segurança.
attempt_count, resend_after, resend_count_windowLimitar adivinhação e abuso de reenvio.Atualização atômica; limites configuráveis.
outbox_event_id, delivery_state, created_atRetomar envio após falha e auditar estado operacional.Evitar payload sensível em logs e eventos analíticos.

Para um código numérico de baixa entropia, um hash simples é insuficiente contra tentativa offline. Proposta: gerar por fonte criptograficamente segura e guardar um digest autenticado com segredo do servidor, incluindo identificador e geração no cálculo. O esquema exato, rotação de chave e retenção precisam de revisão de Segurança. O código em claro só existe pelo tempo necessário para compor a mensagem; o desenho da outbox deve proteger seu payload em repouso e limitar acesso.

Transação: bloquear a tentativa; validar cota; avançar generation; gravar digest/expiração e evento de outbox; confirmar. Um índice/condição de unicidade e o bloqueio evitam duas gerações concorrentes. O worker deve tolerar entrega “ao menos uma vez” sem produzir dois e-mails para o mesmo evento, usando ID do evento e deduplicação disponível. Exatamente uma entrega externa não é garantível sem suporte do provedor.

6. Parâmetros de segurança para discussão

Parâmetro ilustrativoValor inicial propostoAntes de aprovar
Validade do código10 minutosValidar usabilidade, risco e comportamento atual.
Intervalo de reenvio60 segundosValidar custo, latência do e-mail e suporte.
Cota de reenvio3 em 15 minutos por tentativa, com limites adicionais por origem.Ajustar a tráfego legítimo e proteção contra abuso sem bloquear usuários compartilhando IP.
Tentativas de confirmaçãoAté 5 por geração.Revisar tamanho do código, bloqueio e recuperação segura.

Aplicar limites no servidor, com contadores persistentes e política de retenção definida. Não expor código, digest, token de sessão ou e-mail completo em logs, eventos, traces, URL ou respostas. Usar TLS, escopos mínimos para o worker, controle de acesso aos segredos e mensagens que não permitam descobrir contas. Revisar acessibilidade da contagem regressiva e o texto “somente o código mais recente funciona”.

7. Falhas e comportamento esperado

CenárioComportamento previstoAlerta / recuperação
Duplo toque ou pedidos concorrentesUma geração e um evento; replay devolve a mesma resposta lógica.Teste de corrida e métrica de deduplicação.
Banco indisponível antes do commitNenhum código novo; resposta temporária, sem afirmar envio.Retentativa do cliente com a mesma chave.
Fila ou provedor indisponível após commitEvento durável aguarda retentativas; código antigo já está inválido.Alerta de atraso/falha e opção de novo reenvio após janela. Não ocultar falha persistente.
Evento duplicado ou fora de ordemWorker descarta evento já enviado ou geração obsoleta.Registrar contagem, sem dados pessoais.
Confirmar enquanto há reenvioBloqueio/compare-and-swap decide uma ordem; não confirmar código que já foi substituído.Teste de concorrência e mensagem de recuperação.

Trade-off explícito: a geração anterior é invalidada quando o novo pedido é gravado, antes de termos prova de entrega do e-mail. Isso mantém um só código válido, mas uma pane do provedor pode deixar a pessoa temporariamente sem código utilizável. A outbox durável, as retentativas, o monitoramento e uma recuperação clara são condições para aceitar a decisão.

8. Observabilidade e operação

Instrumentar resend_requested, resend_queued, resend_sent, resend_failed, resend_limited, verification_succeeded e latência do pedido até envio. Agregar por versão, ambiente e motivo técnico; não usar e-mail, código ou identificadores de alta cardinalidade como rótulos. Correlacionar eventos por ID opaco com acesso controlado.

Painel: taxa de envio, tempo p95 da fila, falhas do provedor, reenvios por cadastro e conclusão após reenvio. Definir SLO e limiares de alerta com dados reais antes do lançamento; os valores desta RFC não são compromisso operacional. Criar procedimento para fila parada, excesso de 429 e aumento de chamados.

9. Implantação, rollback e migração

  1. Fazer migração aditiva e suportar temporariamente registros antigos. Definir o tratamento de códigos já emitidos antes de ativar o novo verificador.
  2. Ativar por flag em ambiente de teste, depois em parcelas ilustrativas de 5%, 25% e 100% dos novos cadastros. Medir erros, atraso de envio e conclusão em cada etapa.
  3. Se a falha crescer, pausar novos reenvios pelo caminho novo. O rollback deve manter a verificação de desafios já emitidos até expirarem ou prover migração segura; simplesmente desligar o verificador poderia prender usuários.
  4. Remover caminho legado apenas depois da janela de expiração, confirmação das métricas e revisão operacional.

10. Plano de validação

CamadaProva obrigatória
UnidadeExpiração, uso único, cota, cooldown, cálculo de digest e relógio na borda do prazo.
IntegraçãoCommit conjunto de geração/outbox, falha do banco, retentativa e deduplicação do worker.
ConcorrênciaDois pedidos com mesma chave, chaves diferentes, duplo toque e corrida entre confirmar/reenvio.
Segurança e privacidadeEnumeração de contas, adivinhação de código, ausência de segredos em logs/traces e permissão dos segredos.
Fluxo completoCelular, conexão lenta, provedor atrasado, leitor de tela, migração e smoke test após ativação.

11. Alternativas consideradas

  • Desabilitar só o botão: melhora a interface, mas não impede pedidos concorrentes, replay ou clientes alternativos. Insuficiente sozinho.
  • Enviar diretamente na requisição HTTP: é simples, mas acopla latência/disponibilidade do provedor ao cadastro e dificulta recuperação após falha. Rejeitado como padrão desta proposta.
  • Outbox durável + worker: escolhida por consistência entre mudança de estado e intenção de envio, com maior custo operacional. Confirmar se a infraestrutura real já oferece equivalente.

12. Pendências para aprovação

  1. Mapear serviços, tabelas, filas, provedor e contratos existentes; ajustar a proposta ao que já opera na bLOw.
  2. Fechar TTL, tamanho do código, cotas, janela de idempotência e retenção com Engenharia, Produto e Segurança.
  3. Definir como proteger o payload da outbox, como rotacionar segredo e como excluir dados ao fim da retenção.
  4. Especificar a compatibilidade dos desafios já emitidos e o runbook de falha do provedor.
  5. Registrar aprovação técnica, revisão de Segurança/Privacidade e decisão final nesta RFC antes de desenvolver.

Referências técnicas

OWASP · Email Validation and Verification Cheat Sheet; OWASP · Multifactor Authentication Cheat Sheet; NIST SP 800-63B. Os parâmetros numéricos deste exemplo são propostas locais, não números exigidos por essas fontes.