Skip to main content

Autenticar com e-mail e senha — API

Autentica um usuário com e-mail e senha. Em sucesso sem segundo fator, cria uma sessão e devolve seus dados diretamente (session_id, session_expires_at, user_id) — não existe mais um authorization_code emitido por este endpoint. Se a conta tem TOTP ou SafeID habilitado, a rota devolve um desafio de segundo fator em vez de autenticar; o cliente conclui em /authentication/totp/verify ou /authentication/safe-id/verify.

Funcionamento​

  1. Valida a origem da requisição (IdentityOriginGuard).
  2. Aplica rate limit (por IP e por e-mail).
  3. Valida o corpo (email, password, client_id).
  4. Normaliza o e-mail (trim + minúsculas) e busca o usuário ativo.
  5. Se a conta está bloqueada no momento, recusa sem sequer conferir a senha.
  6. Confere a senha (bcrypt). Errada: registra a falha (pode iniciar ou agravar um bloqueio progressivo) e recusa. Certa: zera o contador de falhas.
  7. Se a credencial tem TOTP habilitado, emite um desafio TOTP. Senão, se tem SafeID habilitado, emite um desafio SafeID. Senão, cria a sessão (assurance: PASSWORD) e autentica.

Endpoints​

MétodoRotaDescrição
POST/v1/authentication/loginAutentica por e-mail/senha ou inicia um desafio de MFA

Versão: v1

Swagger: POST /authentication/login · Rota (Dev): http://localhost:3000/v1/authentication/login

Lógica de decisão da rota (validações, bloqueio e MFA → desfechos):

Permissões​

Rota pública (não exige token). O controle de acesso é por origem (IdentityOriginGuard) e por rate limit.

RotaGuardsAcesso
POST /authentication/loginIdentityOriginGuardPúblico, apenas de uma origem permitida

IdentityOriginGuard aceita a requisição quando o header Origin está na lista IDENTITY_ALLOWED_ORIGINS ou quando é um domínio próprio de uma organização (confirmado por uma chamada síncrona ao serviço de Organization); qualquer falha nessa confirmação nega o acesso. Sem header Origin, a requisição é sempre negada.

Headers​

HeaderObrigatórioDescrição
Content-TypeSimapplication/json
OriginSimValidado pelo IdentityOriginGuard; ausente ou não permitido → 403
user-agentNãoGravado na sessão e na auditoria (default "unknown")
x-device-idNãoIdentificador de dispositivo, gravado na sessão/desafio

Path parameters​

Nenhum — não há mais tenant no path desta rota.

Query parameters​

Nenhum.

Body​

IdentityAuthenticationLoginRequest:

json
{
"email": "usuario@exemplo.com",
"password": "SenhaForte123!",
"client_id": "web-portal"
}
CampoTipoObrigatórioValidação
emailstringSim@IsEmail
passwordstringSim@IsString @Length(8, 128)
client_idstringSim@IsString @Length(1, 128)

Response​

200 — autenticado (IdentityAuthenticationResponse):

json
{
"session_id": "b3f1...-uuid",
"session_expires_at": "2026-10-24T12:00:00.000Z",
"user_id": "8f2a...-uuid"
}

200 — desafio TOTP:

json
{ "mfa_type": "totp", "token": "opaque-challenge-token", "expires_at": "2026-09-24T12:10:00.000Z" }

200 — desafio SafeID:

json
{
"mfa_type": "safe_id",
"authorization_url": "https://pscsafeweb.safewebpss.com.br/.../authorize?...",
"state": "b6b6...-uuid",
"expires_at": "2026-09-24T12:10:00.000Z"
}

Nos dois casos de desafio, o cliente conclui em MFA. Não há mais campo authorization_code nesta resposta.

Erros​

Classe de erroerrorCodeStatusQuando ocorre
(validação de payload)BAD_REQUEST400corpo inválido/incompleto
ForbiddenAction (IdentityOriginGuard)IDENTITY_AUTH_ORIGIN_FORBIDDEN403Origin ausente ou não permitida
IdentityInvalidCredentialsErrorIDENTITY_INVALID_CREDENTIALS401usuário não encontrado ou senha errada (sem bloqueio)
IdentityAccountLockedErrorIDENTITY_ACCOUNT_LOCKED423conta com bloqueio temporário ativo
IdentityAccountLockedErrorIDENTITY_ACCOUNT_PERMANENTLY_LOCKED423conta com bloqueio permanente (5ª falha na janela)
ThrottlerExceptionRATE_LIMIT_EXCEEDED429limite de requisições excedido (por IP ou por e-mail)

O Swagger do controller documenta apenas o errorCode IDENTITY_ACCOUNT_LOCKED no exemplo de 423; o caso permanente (IDENTITY_ACCOUNT_PERMANENTLY_LOCKED) usa o mesmo status mas não está no exemplo — divergência de documentação, não de comportamento.

Regras de negócio​

IDRegraComportamento esperado
RN-01E-mail é normalizado antes da buscatrim() + minúsculas; a busca usa o e-mail já normalizado
RN-02Usuário inexistente não distingue de senha erradaambos → 401 IDENTITY_INVALID_CREDENTIALS, sem revelar qual falhou
RN-03Bloqueio é checado antes da senhaconta já bloqueada → 423 imediato, sem gastar uma tentativa nova
RN-04Bloqueio de conta é progressivo, não planover tabela de bloqueio abaixo
RN-05Sucesso zera o contador de falhasfailedLoginAttempts, lockedUntil e lastFailedLoginAt voltam a 0/null
RN-06TOTP tem prioridade sobre SafeIDse a credencial tiver os dois habilitados, o desafio emitido é o de TOTP
RN-07Sem MFA, a sessão é criada com assurance: PASSWORDtokens emitidos depois carregam esse nível até um step-up elevar para MFA
RN-08Desafios de MFA emitidos aqui não persistem o token em clarosó o hash SHA-256 do token vai para o banco; o valor puro só existe na resposta HTTP

Bloqueio progressivo (constantes fixas no código, não configuráveis por variável de ambiente):

Tentativa (dentro da janela de retenção)Efeito
1ª e 2ªSem bloqueio
3ªBloqueio temporário de 10 minutos
4ªBloqueio temporário de 15 minutos
5ª ou maisBloqueio permanente (lockedUntil = 9999-12-31T23:59:59.000Z)

A janela de retenção das tentativas é de 30 minutos enquanto failedLoginAttempts < 4, e de 60 minutos a partir da 4ª falha; fora da janela, o contador reinicia em zero. Um login correto também zera o contador imediatamente.

Compliance​

Órgão / normaExigênciaComo a rota atende
LGPDminimização; não expor se a conta existeresposta idêntica para e-mail inexistente e senha errada; e-mail normalizado, nunca logado em claro fora de auditoria
HIPAAtrilha de login (quem/quando/IP/resultado)eventos de auditoria auth.login.failed, auth.login.succeeded e auth.account.locked, com IP e user-agent
ANVISA (indireto)política de bloqueio por tentativas sob controlebloqueio progressivo auditado, sem depender de configuração externa

Variáveis de ambiente​

VariávelUsoDefault
IDENTITY_ALLOWED_ORIGINSlista (CSV) de origens aceitas pelo IdentityOriginGuard[]
IDENTITY_TOTP_CHALLENGE_TTL_MINUTESvalidade do desafio TOTP emitido aqui10 min
IDENTITY_SAFE_ID_LIFETIME_SECONDSvalidade do desafio SafeID emitido aqui600s
IDENTITY_SESSION_LIFETIME_DAYSvalidade da sessão criada em caso de sucesso30 dias
IDENTITY_PASSWORD_HASH_SALT_ROUNDScusto do bcrypt usado para conferir a senha12

Os limiares do bloqueio progressivo (3ª/4ª/5ª tentativa, janelas de 30/60 min) e os limites de rate limit (7 requisições / 60s) não são variáveis de ambiente — são constantes no código.

Tempo médio de resposta​

A confirmar — responsável: time de Identity; data: 24/09/2026. Não há medição publicada; não foi executado neste levantamento.

Requisitos não funcionais​

RequisitoDefinição
IdempotênciaNão — cada chamada conta para o rate limit e para o contador de falhas
Rate limit7 requisições / 60s, em dois baldes simultâneos: por IP e por e-mail normalizado
CacheNão
AuditoriaSim — auth.login.failed, auth.login.succeeded, auth.account.locked

Relacionado​

  • 🖥️ Tela: A confirmar — responsável: time de frontend; data: 24/09/2026. (página de interface ainda não localizada neste levantamento, que cobriu apenas o backend)
  • 📂 Módulo: Authentication
  • 🔑 Próximo passo: Concluir com MFA (quando a resposta trouxer um desafio)