feat: fluxo de recuperacao de senha - #25
Merged
Merged
Conversation
Nao existia "esqueci minha senha". Quem esquecesse a senha so tinha duas saidas: magic link (que exige SMTP configurado) ou pedir a um admin para redefinir. Numa instalacao sem SMTP, o usuario ficava permanentemente travado - e o admin tambem, se fosse ele quem esqueceu. Backend: - POST /auth/forgot-password e /auth/reset-password, com o mesmo rate limit das demais rotas de auth. - Token de 256 bits guardado como HASH sha256, nao em texto puro: se o banco vazar, os tokens em transito nao servem pra tomar contas. - Resposta identica exista ou nao o e-mail (inclusive para conta inativa), senao o endpoint viraria um verificador de contas cadastradas. A excecao e SMTP fora do ar, que precisa ser explicito - sem isso a pessoa fica esperando um e-mail que nunca vem. - Um pedido novo invalida os anteriores; token expira em 60 min e e de uso unico. - Redefinir a senha destrava a conta bloqueada por tentativas: quem esqueceu a senha e errou ate bloquear nao pode ficar preso esperando. - Indice TTL em password_reset_tokens: o Mongo limpa os vencidos sozinho, ja que nao ha rotina de expurgo no app. Frontend: telas /forgot-password e /reset-password, link no login e aviso de sucesso ao voltar. Textos em pt/en. Validado com stub nos casos que importam (11 checagens): nao enumera contas, token so como hash, senha curta/token invalido/expirado recusados, uso unico, e o destravamento da conta.
Contributor
There was a problem hiding this comment.
Pull request overview
Implementa um fluxo completo de recuperação de senha (sem depender de magic link) para destravar usuários em instalações sem SMTP/magic link, adicionando endpoints, telas e traduções, além de armazenamento seguro de tokens (hash) e expiração automática via TTL no MongoDB.
Changes:
- Adiciona endpoints
/auth/forgot-passworde/auth/reset-passwordcom criação/validação de token e envio de e-mail. - Cria telas públicas
/forgot-passworde/reset-password, com navegação/feedback no login. - Inclui chaves de i18n (PT/EN) e índices Mongo (incluindo TTL) para
password_reset_tokens.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/services/api.js | Adiciona métodos do client para forgot/reset password. |
| frontend/src/pages/auth/ResetPassword.jsx | Nova tela para redefinir senha via token. |
| frontend/src/pages/auth/Login.jsx | Link “Esqueci minha senha” e mensagem de sucesso pós-reset. |
| frontend/src/pages/auth/ForgotPassword.jsx | Nova tela para solicitar link de recuperação. |
| frontend/src/i18n/locales/pt.json | Textos PT para forgot/reset. |
| frontend/src/i18n/locales/en.json | Textos EN para forgot/reset. |
| frontend/src/App.jsx | Rotas públicas para forgot/reset. |
| backend/src/modules/auth/email.service.js | Novo e-mail de reset de senha. |
| backend/src/modules/auth/auth.service.js | Lógica de criação/validação de token e reset de senha. |
| backend/src/modules/auth/auth.routes.js | Novas rotas Fastify para forgot/reset. |
| backend/src/db/indexes.js | Índices para password_reset_tokens (inclui TTL). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+111
to
+118
| "reset": { | ||
| "title": "New password", | ||
| "subtitle": "Choose a password to get back into your account.", | ||
| "submit": "Reset password", | ||
| "success": "Password reset successfully. You can now sign in with your new password.", | ||
| "invalidLink": "Invalid link", | ||
| "invalidLinkHelp": "This reset link isn't valid. Request a new one." | ||
| } |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
O problema
Não existia "esqueci minha senha". Quem esquecesse tinha duas saídas: magic link (que exige SMTP configurado) ou pedir a um admin para redefinir. Numa instalação sem SMTP, o usuário ficava permanentemente travado — e o admin também, se fosse ele quem esqueceu.
Decisões de segurança
Token guardado como hash. O token tem 256 bits e é gravado como SHA-256, não em texto puro. Se o banco vazar, os tokens em trânsito não servem para tomar contas — mesmo raciocínio de não guardar senha em texto puro. (SHA-256 basta aqui: o token é aleatório, não há o que forçar por dicionário como numa senha escolhida por pessoa.)
Não enumera contas. A resposta é idêntica exista ou não o e-mail, inclusive para conta inativa. Sem isso, o endpoint viraria um verificador de e-mails cadastrados. A única exceção é SMTP fora do ar, que precisa ser explícito — senão a pessoa fica esperando um e-mail que nunca vai chegar.
Uso único, prazo curto, e um pedido invalida o anterior. Token expira em 60 minutos; um novo pedido marca os antigos como usados, então só o link mais recente funciona.
Redefinir destrava a conta. Quem esqueceu a senha provavelmente errou várias vezes e acionou o bloqueio de 15 minutos do PR #23. Não faria sentido redefinir a senha e continuar preso esperando o tempo passar.
Índice TTL em
password_reset_tokens— o Mongo limpa os vencidos sozinho, já que não há rotina de expurgo no app.Frontend
Telas
/forgot-passworde/reset-password, link discreto no login (só no modo senha, não no magic link) e aviso de sucesso ao voltar para o login. Textos em PT e EN. Link sem token mostra uma tela de erro com o caminho de volta, em vez de um formulário que falharia no envio.Validação
Testei a lógica com stub, cobrindo os casos que realmente importam:
As três telas foram verificadas no navegador (formulário, nova senha, e link sem token).
Test plan