fix(frontend): renovação automática de sessão — achado real reportado pelo usuário
Achado real: ACCESS_TOKEN_TTL = 15min e apiFetch nunca tentava nenhum refresh — qualquer reload (ou até só navegar) depois de 15min logado batia 401 em toda Server Component que chamasse a API, e o erro (ApiError cru, JSON da API) subia sem tratamento nenhum até virar a tela de erro genérica do Next.js. Reportado pelo usuário: "Ctrl+F5" depois de um tempo deu "Token de acesso inválido ou expirado". apps/frontend/src/middleware.ts (novo) decodifica o exp do access token guardado no cookie (só leitura, sem verificar assinatura — isso continua sendo a API) e, se faltar menos de 60s pra vencer, chama POST /auth/refresh antes da Server Component rodar, gravando o cookie novo tanto na resposta quanto na própria requisição (padrão documentado do Next.js — sem isso a MESMA requisição que disparou o refresh ainda leria o cookie velho). Rede de segurança pro caso do refresh token também ter vencido/sido revogado: apps/frontend/src/app/error.tsx detecta uma mensagem de sessão expirada, desloga e manda pro /login sozinho, em vez de mostrar a tela crua de erro. Tem que ficar na raiz de app/, não dentro de app/app/ ou app/platform/ — error.tsx nunca pega erro do próprio layout do mesmo segmento (achado testando o primeiro corte). Testado ponta a ponta com refresh token de verdade: token forjado pra vencer em 20s + refresh token real → renovação automática confirmada, página protegida carrega normal. Com refresh token inválido de propósito → confirmado o fallback: error boundary detecta, desloga e redireciona pro login sozinho. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
40
TODO.md
40
TODO.md
@@ -1788,6 +1788,46 @@ secao 96-103, 124, 169) + achado real de autorização em `/ai/models`
|
||||
mobile — logos aparecendo corretos em todos, sem o artefato de
|
||||
retângulo branco no Handix
|
||||
|
||||
## PHASE 51 — Renovação automática de sessão (achado real, reportado pelo
|
||||
usuário: `Ctrl+F5` depois de um tempo logado quebrava com 401 cru)
|
||||
- [x] achado real: `ACCESS_TOKEN_TTL = "15m"` (secao 148) e `apiFetch`
|
||||
nunca tentava nenhum refresh — qualquer reload (ou até só navegar)
|
||||
depois de 15min logado batia 401 em toda Server Component que
|
||||
chamasse a API, e o erro (`ApiError` cru, JSON da API) subia sem
|
||||
tratamento nenhum até virar a tela de erro genérica do Next.js. Não
|
||||
era um caso raro: qualquer sessão de teste mais longa que 15min
|
||||
batia nisso
|
||||
- [x] `apps/frontend/src/middleware.ts` (novo) — decodifica o `exp` do
|
||||
access token guardado no cookie (sem verificar assinatura, só
|
||||
leitura — a verificação de verdade continua sendo feita pela API) e,
|
||||
se faltar menos de 60s pra vencer, chama `POST /auth/refresh` com o
|
||||
refresh token *antes* da Server Component rodar, gravando o cookie
|
||||
novo tanto na resposta (`response.cookies`) quanto na própria
|
||||
requisição (`request.cookies`, via `NextResponse.next({ request })`)
|
||||
— sem isto, a MESMA requisição que disparou o refresh ainda leria o
|
||||
cookie velho (padrão documentado do Next.js pra refresh de auth em
|
||||
middleware que precisa valer já na requisição atual, não só na
|
||||
próxima)
|
||||
- [x] Rede de segurança pro caso do refresh token TAMBÉM já ter vencido/
|
||||
sido revogado (sessão parada por mais de 30 dias, ou logout em outro
|
||||
lugar): `apps/frontend/src/app/error.tsx` (Error Boundary do App
|
||||
Router) detecta uma mensagem de sessão expirada/401, desloga
|
||||
(`POST /api/logout`) e manda pro `/login` sozinho, em vez de mostrar
|
||||
a tela crua "Application error: a server-side exception...". Tem
|
||||
que ficar na raiz de `app/`, não dentro de `app/app/` ou
|
||||
`app/platform/` — `error.tsx` nunca pega erro do PRÓPRIO layout do
|
||||
mesmo segmento (é o layout de `/app`/`/platform` que chama
|
||||
`/auth/me` e lança o erro), só de layouts/páginas aninhados abaixo
|
||||
(achado testando: o primeiro corte com um `error.tsx` por segmento
|
||||
não pegava nada, confirmado só depois de mover pra raiz)
|
||||
- [x] Testado ponta a ponta com refresh token de verdade: token forjado
|
||||
pra vencer em 20s + refresh token real → renovação automática
|
||||
confirmada (token novo no cookie, página protegida carrega normal,
|
||||
sem nenhum 401 visível). Com refresh token inválido de propósito →
|
||||
confirmado o fallback: erro 500 na resposta inicial (esperado, SSR),
|
||||
mas o error boundary do client detecta, desloga e redireciona pro
|
||||
login sozinho — sem crash na tela pro usuário
|
||||
|
||||
---
|
||||
|
||||
## Riscos conhecidos
|
||||
|
||||
Reference in New Issue
Block a user