Perfil e segurança do próprio usuário — API
Permite ao usuário autenticado ler e atualizar seus próprios dados: perfil (nome, telefone, CPF,
data de nascimento), registro profissional (CRM ou licença estrangeira), configurações de
segurança (e-mail verificado, e-mail de recuperação) e verificação de e-mail. Todas as rotas atuam
sobre o próprio subject do access token — não recebem userId na URL.
Funcionamento
- Ler perfil (
GET /users/me): busca o usuário ativo peloiddo token; usuário inativo ou inexistente é tratado como não encontrado. - Atualizar perfil (
PATCH /users/me): aceitadisplay_name,phone_number,cpf_numberebirth_date, todos opcionais, mas ao menos um deve vir preenchido. Só os campos enviados são comparados com o valor atual; um campo idêntico ao já salvo não gera mudança nem evento de auditoria. - Substituir registro profissional (
PUT /users/me/professional-license): valida a UF do CRM contra a tabela de estados brasileiros e impede que a mesma conta tenha simultaneamente um CRM brasileiro e uma licença estrangeira. - Ler configurações de segurança (
GET /users/me/security): devolve e-mail, se o e-mail primário foi verificado e o e-mail de recuperação atual. - Solicitar verificação do e-mail primário (
POST /users/me/email-verification): se o e-mail já está verificado, não reenvia nada e informa isso na resposta; senão emite um token de uso único e envia e-mail transacional. - Definir/trocar e-mail de recuperação (
PATCH /users/me/recovery-email): sempre envia um e-mail de confirmação para o novo endereço antes de ele valer; o e-mail de recuperação atual só muda quando o link de confirmação é usado (fluxo fora desta página — verauthentication). - Limpar e-mail de recuperação (
DELETE /users/me/recovery-email): mesmo endpoint interno de troca, chamado comrecoveryEmail: null; remove imediatamente o e-mail de recuperação e qualquer confirmação pendente.
Endpoints
| Método | Rota | Descrição |
|---|---|---|
| GET | /v1/users/me | Lê o perfil do usuário autenticado |
| PATCH | /v1/users/me | Atualiza campos do próprio perfil |
| PUT | /v1/users/me/professional-license | Substitui CRM/UF ou licença estrangeira do próprio usuário |
| GET | /v1/users/me/security | Lê e-mail, verificação de e-mail e e-mail de recuperação |
| POST | /v1/users/me/email-verification | Solicita o envio do e-mail de verificação do e-mail primário |
| PATCH | /v1/users/me/recovery-email | Solicita a troca do e-mail de recuperação (envia confirmação) |
| DELETE | /v1/users/me/recovery-email | Remove o e-mail de recuperação |
Versão: v1
Swagger: Identity — User administration and profile · Rota (Dev): http://localhost:3000/v1/users/me
Lógica de decisão da rota mais complexa deste grupo (PATCH /users/me):
Permissões
Rotas privadas ao próprio usuário: exigem apenas um access token OAuth válido
(IdentityOAuthAccessTokenGuard). Não há permissão nomeada adicional — o escopo é sempre o
subject do token, nunca um userId de terceiro.
| Rota | Guards | Acesso |
|---|---|---|
GET/PATCH /users/me, PUT /users/me/professional-license, GET /users/me/security, POST /users/me/email-verification, PATCH/DELETE /users/me/recovery-email | IdentityOAuthAccessTokenGuard | Qualquer usuário autenticado, apenas sobre si mesmo |
Headers
| Header | Obrigatório | Descrição |
|---|---|---|
Authorization | Sim | Bearer <access_token> |
Content-Type | Sim (PATCH/PUT) | application/json |
Body
PATCH /users/me — IdentityOwnUserProfileUpdateRequest:
json{"display_name": "Ana Beatriz Silva","phone_number": "+5511999999999","cpf_number": "12345678900","birth_date": "1990-05-20"}
| Campo | Tipo | Obrigatório | Validação |
|---|---|---|---|
display_name | string | Não | 1–160 caracteres |
phone_number | string | Não | string, até 20 caracteres |
cpf_number | string | Não | string, até 14 caracteres |
birth_date | string (data ISO) | Não | data válida, não pode ser futura |
Ao menos um campo deve ser enviado; corpo totalmente vazio é rejeitado com
IDENTITY_USER_PROFILE_UPDATE_EMPTY.
PUT /users/me/professional-license — IdentityUserProfessionalLicenseRequest:
json{ "medical_license": 123456, "medical_license_state": "SP", "foreign_medical_license": null }
| Campo | Tipo | Obrigatório | Validação |
|---|---|---|---|
medical_license | int | null | Não | inteiro ≥ 1; deve vir junto com medical_license_state |
medical_license_state | string | null | Não | exatamente 2 letras; UF deve existir na tabela de estados |
foreign_medical_license | string | null | Não | até 50 caracteres; não pode coexistir com medical_license |
PATCH /users/me/recovery-email — IdentityRecoveryEmailRequest:
json{ "recovery_email": "recuperacao@exemplo.com" }
| Campo | Tipo | Obrigatório | Validação |
|---|---|---|---|
recovery_email | string | Sim | formato de e-mail |
DELETE /users/me/recovery-email não recebe corpo.
Response
200 — GET /users/me (IdentityUserProfileResponse):
json{"id": "8f2a...-uuid","displayName": "Ana Beatriz Silva","email": "ana.silva@exemplo.com","birthDate": "1990-05-20","cpfNumber": "12345678900","foreignMedicalLicense": null,"medicalLicense": 123456,"medicalLicenseState": "SP","phoneNumber": "+5511999999999"}
200 — PATCH /users/me, PUT .../professional-license (IdentityUserChangeResponse):
json{ "message": "User profile updated." }
200 — GET /users/me/security (IdentityUserSecurityResponse):
json{ "email": "ana.silva@exemplo.com", "email_verified": false, "recovery_email": null }
200 — POST /users/me/email-verification (IdentityEmailVerificationRequestResponse):
json{ "sent": true, "already_verified": false }
200 — PATCH /users/me/recovery-email (IdentityRecoveryEmailChangeResponse):
json{ "pendingConfirmation": true }
Erros
| Classe de erro | errorCode | Status | Quando ocorre |
|---|---|---|---|
IdentityUserProfile (validação) | IDENTITY_USER_PROFILE_INVALID | 400 | nome resultante vazio após normalização |
IdentityUserProfileUpdate (validação) | IDENTITY_USER_PROFILE_UPDATE_EMPTY | 400 | PATCH /users/me sem nenhum campo |
IdentityUserBirthDate/IdentityUserDisplayName (validação) | IDENTITY_USER_BIRTH_DATE_INVALID / IDENTITY_USER_DISPLAY_NAME_INVALID | 400 | valor de campo inválido |
IdentityProfessionalLicense (validação) | IDENTITY_PROFESSIONAL_LICENSE_INVALID | 400 | CRM sem UF (ou vice-versa), ou CRM + licença estrangeira juntos |
IdentityUserNotFoundError | IDENTITY_USER_NOT_FOUND | 404 | usuário do token não existe mais ou está inativo |
ValidationError (verificação de e-mail) | IDENTITY_EMAIL_VERIFICATION_USER_UNAVAILABLE | 400 | usuário ou credencial indisponível para o fluxo de verificação |
| — | IDENTITY_INVALID_OAUTH_ACCESS_TOKEN | 401 | token ausente, inválido, expirado ou revogado |
Regras de negócio
| ID | Regra | Comportamento esperado |
|---|---|---|
| RN-01 | Atualização parcial e idempotente | só os campos enviados e efetivamente diferentes do valor salvo geram mudança |
| RN-02 | Nome não pode ficar vazio | display_name é normalizado (trim); vazio após normalização é rejeitado |
| RN-03 | CRM e UF são um par | informar medical_license sem medical_license_state (ou o contrário) é erro |
| RN-04 | CRM brasileiro e licença estrangeira são mutuamente exclusivos | a mesma conta não pode ter os dois preenchidos ao mesmo tempo |
| RN-05 | UF do CRM é validada contra o catálogo de estados | UF inexistente no catálogo brasileiro é rejeitada |
| RN-06 | Trocar o e-mail de recuperação nunca é imediato | o valor só passa a valer quando o link enviado ao novo endereço é confirmado |
| RN-07 | Pedir verificação de e-mail já verificado não reenvia nada | resposta sent: false, already_verified: true |
Compliance
| Órgão / norma | Exigência | Como a rota atende |
|---|---|---|
| LGPD | direito de retificação (Art. 18, III) | o próprio titular corrige nome, telefone, CPF e data de nascimento sem depender de terceiro |
| LGPD | minimização | resposta de GET /users/me/security não inclui senha nem segredos de MFA |
Variáveis de ambiente
| Variável | Uso | Obrigatória |
|---|---|---|
IDENTITY_EMAIL_VERIFICATION_URL (nome de config identity.authentication.emailVerificationUrl) | base da URL enviada no e-mail de verificação | Sim |
Config identity.authentication.emailVerificationTtlInHours | validade do token de verificação de e-mail | Sim |
Config identity.authentication.passwordRecoveryUrl / passwordRecoveryTtlInMinutes | base da URL e validade do link de confirmação do e-mail de recuperação | Sim |
Requisitos não funcionais
| Requisito | Definição |
|---|---|
| Idempotência | PATCH /users/me é idempotente para o mesmo payload (sem mudança, sem evento) |
| Rate limit | Não identificado nestas rotas especificamente |
| Auditoria | Sim — identity.user.profile_updated, identity.user.professional_license_changed, auth.email.verification_requested, auth.recovery_email.change_requested/cleared |
Relacionado
- 🖥️ Tela:
A confirmar — responsável: time de frontend; data: 24/09/2026.(levantamento cobriu apenas o backend) - 📂 Módulo: Usuário
- 🔑 MFA (TOTP e SafeID) — resetar TOTP de um terceiro é feito em Gerenciar usuário