feat(auth): aumenta sessão web pra 12h (pedido do usuário: call center, agente não pode cair no meio do turno)
Access token JWT (packages/auth/src/tokens.ts) e o teto absoluto do cookie de sessão (duplicado em login/post-login/select-tenant/middleware) subiram de 15min/8h pra 12h. O refresh silencioso do middleware continua existindo e deslizando a sessão pra frente a cada requisição — 12h agora é só o teto de segurança pra quando esse refresh falhar (aba parada, logout em outro lugar), não o intervalo real de renovação. Verificado com login real: token decodificado mostra exp-iat = 12h exatas. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
@@ -29,7 +29,12 @@ export async function POST(request: Request) {
|
|||||||
sameSite: "lax",
|
sameSite: "lax",
|
||||||
secure: process.env.NODE_ENV === "production",
|
secure: process.env.NODE_ENV === "production",
|
||||||
path: "/",
|
path: "/",
|
||||||
maxAge: 60 * 60 * 8,
|
// Call center: turno de agente pode passar de 8h — 12h é o teto
|
||||||
|
// absoluto do cookie mesmo com o refresh silencioso do middleware
|
||||||
|
// deslizando ele pra frente a cada requisição (o mesmo valor está
|
||||||
|
// duplicado em post-login/route.ts, select-tenant/actions.ts e
|
||||||
|
// middleware.ts — os 4 precisam ficar em sincronia).
|
||||||
|
maxAge: 60 * 60 * 12,
|
||||||
});
|
});
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export async function POST() {
|
|||||||
sameSite: "lax",
|
sameSite: "lax",
|
||||||
secure: process.env.NODE_ENV === "production",
|
secure: process.env.NODE_ENV === "production",
|
||||||
path: "/",
|
path: "/",
|
||||||
maxAge: 60 * 60 * 8,
|
maxAge: 60 * 60 * 12,
|
||||||
});
|
});
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export async function chooseTenant(tenantId: string): Promise<{ error: string }
|
|||||||
sameSite: "lax",
|
sameSite: "lax",
|
||||||
secure: process.env.NODE_ENV === "production",
|
secure: process.env.NODE_ENV === "production",
|
||||||
path: "/",
|
path: "/",
|
||||||
maxAge: 60 * 60 * 8,
|
maxAge: 60 * 60 * 12,
|
||||||
});
|
});
|
||||||
|
|
||||||
redirect("/app");
|
redirect("/app");
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export async function middleware(request: NextRequest) {
|
|||||||
sameSite: "lax",
|
sameSite: "lax",
|
||||||
secure: process.env.NODE_ENV === "production",
|
secure: process.env.NODE_ENV === "production",
|
||||||
path: "/",
|
path: "/",
|
||||||
maxAge: 60 * 60 * 8,
|
maxAge: 60 * 60 * 12,
|
||||||
});
|
});
|
||||||
return response;
|
return response;
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ Implementado em `packages/auth`, sobre o schema criado pela migration
|
|||||||
e-mail não existe, e devolve o mesmo erro genérico (`InvalidCredentialsError`)
|
e-mail não existe, e devolve o mesmo erro genérico (`InvalidCredentialsError`)
|
||||||
em qualquer caso de falha — mitiga user-enumeration por diferença de tempo
|
em qualquer caso de falha — mitiga user-enumeration por diferença de tempo
|
||||||
de resposta ou mensagem.
|
de resposta ou mensagem.
|
||||||
- Access token: JWT (HS256, `jose`), TTL 15 minutos, claims `{ sub, sessionId,
|
- Access token: JWT (HS256, `jose`), TTL 12 horas (era 15 minutos até a
|
||||||
tenantId? }`.
|
PHASE 69 — pedido do usuário: turno de agente de call center não pode
|
||||||
|
cair no meio, o refresh silencioso do middleware existe mas 15min forçava
|
||||||
|
ele rodar a cada poucos minutos), claims `{ sub, sessionId, tenantId? }`.
|
||||||
- Refresh token: string opaca aleatória (32 bytes), **nunca** JWT. Só o
|
- Refresh token: string opaca aleatória (32 bytes), **nunca** JWT. Só o
|
||||||
SHA-256 fica salvo em `sessions.refresh_token_hash` — posse do token original
|
SHA-256 fica salvo em `sessions.refresh_token_hash` — posse do token original
|
||||||
é a prova de identidade.
|
é a prova de identidade.
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { createHash, randomBytes } from "node:crypto";
|
import { createHash, randomBytes } from "node:crypto";
|
||||||
import { SignJWT, jwtVerify, type JWTPayload } from "jose";
|
import { SignJWT, jwtVerify, type JWTPayload } from "jose";
|
||||||
|
|
||||||
const ACCESS_TOKEN_TTL = "15m";
|
// Call center: agente logado o turno inteiro sem cair no meio de uma
|
||||||
|
// ligação — 15min (default anterior) forçava refresh silencioso a cada
|
||||||
|
// poucos minutos, arriscando um gap bem no meio de uma chamada.
|
||||||
|
const ACCESS_TOKEN_TTL = "12h";
|
||||||
export const REFRESH_TOKEN_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 dias
|
export const REFRESH_TOKEN_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 dias
|
||||||
|
|
||||||
export interface AccessTokenClaims extends JWTPayload {
|
export interface AccessTokenClaims extends JWTPayload {
|
||||||
|
|||||||
Reference in New Issue
Block a user