Add login and register dialogs, user dropdown menu with profile link and logout option, and integrate with session management hook. Prevent hydration mismatches by conditionally rendering auth elements after mount. Update package-lock.json with new @esbuild dependencies for build tooling.
118 lines
2.8 KiB
TypeScript
118 lines
2.8 KiB
TypeScript
/**
|
|
* Middleware de autenticação
|
|
* Extrai e valida tokens JWT das requisições
|
|
*/
|
|
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
import { cookies } from "next/headers";
|
|
import { verifyAccessToken } from "./jwt";
|
|
import { getSession } from "@/lib/redis/session";
|
|
|
|
/**
|
|
* Extrai token do header Authorization
|
|
* @param request - Request do Next.js
|
|
* @returns Token ou null
|
|
*/
|
|
function extractToken(request: NextRequest): string | null {
|
|
const authHeader = request.headers.get("authorization");
|
|
|
|
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
return null;
|
|
}
|
|
|
|
return authHeader.substring(7); // Remove "Bearer "
|
|
}
|
|
|
|
/**
|
|
* Middleware que requer autenticação
|
|
* Retorna dados do usuário ou NextResponse com erro
|
|
* Suporta tanto Bearer token quanto cookie sessionId
|
|
* @param request - Request do Next.js
|
|
* @returns Dados do usuário autenticado ou NextResponse com erro
|
|
*/
|
|
export async function requireAuth(
|
|
request: NextRequest
|
|
): Promise<
|
|
| { userId: number; username: string }
|
|
| NextResponse<{ success: false; error: string }>
|
|
> {
|
|
// Tentar primeiro com Bearer token
|
|
const token = extractToken(request);
|
|
|
|
if (token) {
|
|
const payload = verifyAccessToken(token);
|
|
|
|
if (payload) {
|
|
return payload;
|
|
}
|
|
}
|
|
|
|
// Se não tiver token no header, tentar com cookie
|
|
try {
|
|
const cookieStore = await cookies();
|
|
const sessionId = cookieStore.get("sessionId")?.value;
|
|
|
|
if (sessionId) {
|
|
const session = await getSession(sessionId);
|
|
|
|
if (session) {
|
|
const tokenPayload = verifyAccessToken(session.accessToken);
|
|
|
|
if (tokenPayload) {
|
|
return tokenPayload;
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
// Se der erro ao acessar cookies, continuar com o erro de token
|
|
}
|
|
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: "Token de autenticação não fornecido ou inválido",
|
|
},
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Middleware opcional de autenticação
|
|
* Retorna dados do usuário se autenticado, null caso contrário
|
|
* Não lança erro se não autenticado
|
|
* @param request - Request do Next.js
|
|
* @returns Dados do usuário ou null
|
|
*/
|
|
export async function optionalAuth(
|
|
request: NextRequest
|
|
): Promise<{ userId: number; username: string } | null> {
|
|
// Tentar primeiro com Bearer token
|
|
const token = extractToken(request);
|
|
|
|
if (token) {
|
|
const payload = verifyAccessToken(token);
|
|
if (payload) {
|
|
return payload;
|
|
}
|
|
}
|
|
|
|
// Tentar com cookie
|
|
try {
|
|
const cookieStore = await cookies();
|
|
const sessionId = cookieStore.get("sessionId")?.value;
|
|
|
|
if (sessionId) {
|
|
const session = await getSession(sessionId);
|
|
|
|
if (session) {
|
|
const tokenPayload = verifyAccessToken(session.accessToken);
|
|
return tokenPayload;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
// Ignorar erros de cookie
|
|
}
|
|
|
|
return null;
|
|
}
|