Skip to main content

Recuperar, redefinir e trocar senha — API

Cobre os três fluxos de senha do Portal 2.0: esqueci minha senha (gera um token e envia e-mail), redefinir com o token (troca a senha usando o link recebido) e trocar a senha estando autenticado (exige a senha atual). Os dois primeiros são públicos; o terceiro exige um access token válido.

Funcionamento​

Esqueci a senha (POST /authentication/password/forgot): busca o usuário pelo e-mail normalizado (ou pelo e-mail de recuperação, se destination: "recovery"); se existir e tiver senha cadastrada, gera um token de recuperação, invalida qualquer desafio de reset anterior ainda não usado, salva o hash do novo token e envia um e-mail com o link. A resposta é sempre genérica de sucesso, exista ou não a conta (anti-enumeração) — inclusive se o envio do e-mail falhar (a falha só é registrada em log).

Redefinir com o token (POST /authentication/password/reset): valida o formato do token e a política de senha antes de tocar no banco; localiza o desafio pelo hash do token, confere que não expirou, troca a senha (bcrypt), zera o contador de falhas de login da conta e invalida o token. Em sucesso, envia um e-mail de confirmação (best-effort).

Trocar a senha autenticado (PATCH /authentication/password): exige a senha atual e a confere antes de trocar; também zera o contador de falhas de login, mas não envia e-mail de confirmação.

Endpoints​

MétodoRotaDescrição
POST/v1/authentication/password/forgotSolicita recuperação: gera token e envia e-mail
POST/v1/authentication/password/resetRedefine a senha usando o token do e-mail
PATCH/v1/authentication/passwordTroca a senha do usuário autenticado

Versão: v1

Swagger:

Rota (Dev): http://localhost:3000/v1/authentication/password...

Lógica de decisão das três rotas:

Permissões​

RotaGuardsAcesso
POST /authentication/password/forgotnenhum (rate limit)Público
POST /authentication/password/resetnenhum (rate limit)Público, mediante posse do token
PATCH /authentication/passwordIdentityOAuthAccessTokenGuardUsuário autenticado (sobre si mesmo)

Nenhuma dessas rotas usa IdentityOriginGuard.

Headers​

HeaderObrigatórioDescrição
Content-TypeSimapplication/json
AuthorizationSim (PATCH /password)Bearer <access_token>

Path parameters​

Nenhum.

Query parameters​

Nenhum.

Body​

Forgot — IdentityPasswordResetRequest:

json
{ "email": "usuario@exemplo.com", "client_id": "web-portal", "destination": "primary" }
CampoTipoObrigatórioValidação
emailstringSim@IsEmail
client_idstringSim@Length(1, 128)
destination"primary" | "recovery"Não@IsIn — para qual e-mail enviar o link

Reset — IdentityPasswordResetConfirmation:

json
{ "token": "reset-token-recebido-por-email", "new_password": "NovaSenha123!", "client_id": "web-portal" }
CampoTipoObrigatórioValidação
tokenstringSim@Length(1, 512)
new_passwordstringSim@Length(8, 128) no DTO — a política real (8 a 16 caracteres, ver Regras) é aplicada depois
client_idstringSim@Length(1, 128)

Trocar autenticado — IdentityPasswordChangeRequest:

json
{ "current_password": "SenhaAtual123!", "new_password": "NovaSenha123!" }
CampoTipoObrigatórioValidação
current_passwordstringSim@IsString (sem limite de tamanho no DTO)
new_passwordstringSim@IsString (a política de senha é aplicada dentro do serviço, não no DTO)

Nenhuma das três rotas tem campo de confirmação/repetição de senha — se o front pedir para repetir, essa checagem é só de interface, não é revalidada no backend.

Response​

Forgot — 200 (sempre, exista ou não a conta):

json
{ "message": "If an account with that email exists, a password reset link has been sent." }

Reset — 200: { "message": "Password reset completed." }

Trocar autenticado — 200: { "message": "Password changed." }

Erros​

RotaClasse de erroerrorCodeStatusQuando ocorre
forgot(validação de payload)BAD_REQUEST400email inválido
forgotThrottlerExceptionRATE_LIMIT_EXCEEDED429limite excedido
reset(validação de formato)IDENTITY_RECOVERY_TOKEN_INVALID400token não tem o formato esperado (base64url, 43 caracteres)
reset(política de senha)INVALID_PASSWORD400new_password fora da política (ver Regras)
resetIdentityInvalidRecoveryTokenErrorIDENTITY_INVALID_RECOVERY_TOKEN401token bem formado mas inexistente, expirado ou já usado
trocar autenticadoIdentityInvalidOAuthAccessTokenErrorIDENTITY_INVALID_OAUTH_ACCESS_TOKEN401access token ausente/inválido
trocar autenticadoIdentityInvalidCredentialsErrorIDENTITY_INVALID_CREDENTIALS401current_password não confere
trocar autenticado(política de senha)INVALID_PASSWORD400new_password fora da política

O token inválido/expirado no reset responde 401, não 404. A mensagem devolvida é "Authentication required" (herdada de UnauthenticatedError) — não existe a string "Invalid or expired reset token" no código atual.

Regras de negócio​

IDRegraComportamento
RN-01Forgot é anti-enumeração totalusuário inexistente, sem senha cadastrada, ou sem e-mail de recuperação confirmado → sempre 200 genérico
RN-02Token de reset é aleatório e guardado por hashrandomBytes(32) codificado em base64url (43 caracteres); persiste o SHA-256 do token, nunca o valor puro
RN-03Token de reset expira por TTLIDENTITY_PASSWORD_RECOVERY_TTL_MINUTES (default 60 min)
RN-04Política de senha: 8 a 16 caracteresmínimo 8, minúscula + maiúscula + dígito + especial (@$!%*?&) — há teto de 16, não é ilimitado
RN-05Falha no envio do e-mail não quebra o forgoterro só é logado; resposta continua 200
RN-06Reset e troca autenticada zeram o bloqueio de loginfailedLoginAttempts, lockedUntil e lastFailedLoginAt voltam a 0/null nos dois casos
RN-07Reset envia e-mail de confirmação pós-trocabest-effort (falha só é logada); e-mail: "Sua senha da Mobilemed foi alterada"
RN-08Trocar autenticado não envia e-mail de confirmaçãodiferente do reset — o serviço de troca autenticada não dispara e-mail nenhum
RN-09Trocar autenticado exige a senha atualsenha atual errada → 401 IDENTITY_INVALID_CREDENTIALS, sem contar como tentativa de bloqueio de login
RN-10Sem confirmação de senha no backendrepetir a senha é validação apenas de interface, não existe campo equivalente no backend

Compliance​

Órgão / normaExigênciaComo a rota atende
LGPDminimização; anti-enumeração; não logar e-mail/token em claroforgot não revela existência da conta; token nunca aparece em log ou resposta de erro
HIPAAsenha forte; token de uso único e expirávelpolítica de senha aplicada por Value Object; token com hash + TTL + invalidação no uso
ANVISA (indireto)redefinição de credencial controlada e auditávelevento de auditoria auth.password.changed (tanto por recuperação quanto por troca autenticada) e auth.password.reset_requested

Variáveis de ambiente​

VariávelUsoDefault
IDENTITY_PASSWORD_RECOVERY_TTL_MINUTESvalidade do token de reset60 min (máx. 1440)
IDENTITY_PASSWORD_RECOVERY_URLURL base do link enviado no e-mailhttp://localhost:4200/password/reset
IDENTITY_PASSWORD_HASH_SALT_ROUNDScusto do bcrypt para o hash da senha12 (mín. 4, máx. 15) — é configurável, não fixo

Tempo médio de resposta​

A confirmar — responsável: time de Identity; data: 24/09/2026. Sem medição publicada.

Requisitos não funcionais​

RequisitoDefinição
IdempotênciaNão — cada forgot gera um novo token; reset/troca consomem o token/verificam a senha atual a cada chamada
Rate limitforgot/reset: 5 requisições / 60s (IP + identidade); PATCH /password não tem @Throttle próprio
CacheNão
AuditoriaSim — auth.password.reset_requested, auth.password.changed

Divergências e lacunas confirmadas​

  • Existem dois errorCode parecidos e fáceis de confundir: IDENTITY_RECOVERY_TOKEN_INVALID (400, formato do token) e IDENTITY_INVALID_RECOVERY_TOKEN (401, token não encontrado/expirado/ já usado).
  • Um erro de configuração da URL do e-mail (IDENTITY_EMAIL_ACTION_URL_INVALID, documentado como 500 no Swagger do forgot) é hoje capturado pelo próprio try/catch de envio de e-mail e nunca chega ao cliente — divergência de documentação, não de comportamento observável.
  • Não existem testes unitários dedicados aos casos de senha; a cobertura vem de um único e2e (identity-authentication-flow.e2e.spec.ts).

Fora do escopo desta página​

O mesmo controller expõe POST /authentication/email/verify e POST /authentication/recovery-email/confirm — confirmação de verificação de e-mail e de troca do e-mail de recuperação, respectivamente. Ambos são fluxos de token público muito parecidos com o reset de senha (mesmo Value Object de token, mesmo erro 401 em caso de token inválido/expirado), mas tratam de identidade, não de senha. A confirmar — responsável: time de Identity; data: 24/09/2026.: se vale a pena uma página própria para esses dois endpoints.

Relacionado​

  • 🖥️ Tela: A confirmar — responsável: time de frontend; data: 24/09/2026. (não localizada neste levantamento, que cobriu apenas o backend)
  • 📂 Módulo: Authentication