src/lib/auth/session.ts
Annotation non disponible
Lance npm run annotate (nécessite ANTHROPIC_API_KEY dans .env.local) pour générer une annotation française par Claude Haiku 4.5.
Concepts détectés — comprends la théorie
JWT / Auth backend
5 occurrencesCe fichier touche au système d'authentification (JWT, session, getSessionFromRequest). Voir le contrat API pour la logique complète.
Voir l'article général
Route API Next.js
1 occurrenceCe fichier est une route API Next.js (App Router). Voir le contrat API complet pour les conventions de réponse et d'auth.
Voir l'article général
4 exports
createSessiongetSessiondeleteSessiongetSessionFromRequest
Code source· typescript
import { SignJWT, jwtVerify } from "jose";
import { cookies } from "next/headers";
import { setRequestUser } from "@/lib/sentry";
const NEXTAUTH_SECRET = process.env.NEXTAUTH_SECRET;
if (!NEXTAUTH_SECRET) {
throw new Error("NEXTAUTH_SECRET is required");
}
const secret = new TextEncoder().encode(NEXTAUTH_SECRET);
type SessionPayload = { userId: string; role: string; tenantId: string | null };
export async function createSession(userId: string, role: string, tenantId?: string) {
const token = await new SignJWT({ userId, role, tenantId: tenantId ?? null })
.setProtectedHeader({ alg: "HS256" })
.setExpirationTime("7d")
.sign(secret);
const cookieStore = await cookies();
cookieStore.set("superapp_session", token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 60 * 60 * 24 * 7,
path: "/",
});
}
export async function getSession() {
const cookieStore = await cookies();
const token = cookieStore.get("superapp_session")?.value;
if (!token) return null;
try {
const { payload } = await jwtVerify(token, secret);
return payload as { userId: string; role: string; tenantId: string | null };
} catch {
return null;
}
}
export async function deleteSession() {
const cookieStore = await cookies();
const isProduction = process.env.NODE_ENV === "production";
cookieStore.set("superapp_session", "", {
httpOnly: true,
secure: isProduction,
sameSite: "lax",
maxAge: 0,
path: "/",
...(isProduction ? { domain: ".wari.pro" } : {}),
});
}
// Mobile : accepte cookie OU Bearer Token
export async function getSessionFromRequest(req: import("next/server").NextRequest) {
const session = await extractSessionFromRequest(req);
// Sentry user context — request-wide tags (override per-capture via ctx).
// Le JWT ne contient pas l'email (payload minimal {userId, role, tenantId}).
// Les helpers `captureError(ctx)` peuvent enrichir avec email au cas par cas.
setRequestUser(session);
return session;
}
async function extractSessionFromRequest(
req: import("next/server").NextRequest,
): Promise<SessionPayload | null> {
// 1. Essayer Bearer Token (Authorization: Bearer <jwt>)
const authHeader = req.headers.get("authorization");
if (authHeader?.startsWith("Bearer ")) {
const bearerToken = authHeader.slice(7);
try {
const { payload } = await jwtVerify(bearerToken, secret);
return payload as SessionPayload;
} catch {
return null;
}
}
// 2. Fallback cookie (web classique)
const token = req.cookies.get("superapp_session")?.value;
if (!token) return null;
try {
const { payload } = await jwtVerify(token, secret);
return payload as SessionPayload;
} catch {
return null;
}
}
import { SignJWT, jwtVerify } from "jose";
import { cookies } from "next/headers";
import { setRequestUser } from "@/lib/sentry";
const NEXTAUTH_SECRET = process.env.NEXTAUTH_SECRET;
if (!NEXTAUTH_SECRET) {
throw new Error("NEXTAUTH_SECRET is required");
}
const secret = new TextEncoder().encode(NEXTAUTH_SECRET);
type SessionPayload = { userId: string; role: string; tenantId: string | null };
export async function createSession(userId: string, role: string, tenantId?: string) {
const token = await new SignJWT({ userId, role, tenantId: tenantId ?? null })
.setProtectedHeader({ alg: "HS256" })
.setExpirationTime("7d")
.sign(secret);
const cookieStore = await cookies();
cookieStore.set("superapp_session", token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 60 * 60 * 24 * 7,
path: "/",
});
}
export async function getSession() {
const cookieStore = await cookies();
const token = cookieStore.get("superapp_session")?.value;
if (!token) return null;
try {
const { payload } = await jwtVerify(token, secret);
return payload as { userId: string; role: string; tenantId: string | null };
} catch {
return null;
}
}
export async function deleteSession() {
const cookieStore = await cookies();
const isProduction = process.env.NODE_ENV === "production";
cookieStore.set("superapp_session", "", {
httpOnly: true,
secure: isProduction,
sameSite: "lax",
maxAge: 0,
path: "/",
...(isProduction ? { domain: ".wari.pro" } : {}),
});
}
// Mobile : accepte cookie OU Bearer Token
export async function getSessionFromRequest(req: import("next/server").NextRequest) {
const session = await extractSessionFromRequest(req);
// Sentry user context — request-wide tags (override per-capture via ctx).
// Le JWT ne contient pas l'email (payload minimal {userId, role, tenantId}).
// Les helpers `captureError(ctx)` peuvent enrichir avec email au cas par cas.
setRequestUser(session);
return session;
}
async function extractSessionFromRequest(
req: import("next/server").NextRequest,
): Promise<SessionPayload | null> {
// 1. Essayer Bearer Token (Authorization: Bearer <jwt>)
const authHeader = req.headers.get("authorization");
if (authHeader?.startsWith("Bearer ")) {
const bearerToken = authHeader.slice(7);
try {
const { payload } = await jwtVerify(bearerToken, secret);
return payload as SessionPayload;
} catch {
return null;
}
}
// 2. Fallback cookie (web classique)
const token = req.cookies.get("superapp_session")?.value;
if (!token) return null;
try {
const { payload } = await jwtVerify(token, secret);
return payload as SessionPayload;
} catch {
return null;
}
}