fix(frontend): tela de "Trocar senha" no primeiro acesso

Achado real reportado pelo usuário testando: todo usuário criado (tenant novo em
Clientes > Tenants, ou convite em Administração > Usuários) nasce com
mustChangePassword: true (agente.md secao 199), mas POST /api/login simplesmente
bloqueava com "use a API /auth/change-password por enquanto" — sem nenhuma tela pra
fazer isso. Todo primeiro login de qualquer conta nova batia nessa parede.

POST /api/login agora grava o cookie de sessão mesmo com mustChangePassword: true
(única forma de chamar /auth/change-password autenticado depois) e devolve o flag pro
client, que manda pra /trocar-senha em vez de mostrar erro. Tela nova pede a senha
temporária + nova senha (2x), chama POST /auth/change-password, e reaproveita a mesma
decisão platform/tenant do login normal (/api/post-login).

Testado ponta a ponta com um usuário de teste de verdade (convidado, nunca logado
antes): login → /trocar-senha → senha trocada → /app direto.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
2026-08-30 10:41:20 -03:00
parent 798aa7b579
commit 4b927e9d9e
8 changed files with 195 additions and 8 deletions

View File

@@ -19,14 +19,11 @@ export async function POST(request: Request) {
const data = await res.json();
if (data.mustChangePassword) {
return NextResponse.json(
{ message: "Senha precisa ser trocada antes do primeiro acesso (use a API /auth/change-password por enquanto)." },
{ status: 403 },
);
}
const response = NextResponse.json({ ok: true });
// Sessão gravada mesmo com senha pendente de troca (secao 199) — só dá
// pra chamar POST /auth/change-password autenticado, então o cookie
// precisa existir antes de mandar pra /trocar-senha. `mustChangePassword`
// devolvido aqui é só o sinal pro client escolher a próxima tela.
const response = NextResponse.json({ ok: true, mustChangePassword: Boolean(data.mustChangePassword) });
response.cookies.set(sessionCookieName(), JSON.stringify({ accessToken: data.accessToken, refreshToken: data.refreshToken }), {
httpOnly: true,
sameSite: "lax",