src/lib/auth/session.ts

function·app·2.7 KB · 89 lignes· Voir l'itinéraire
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.

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;
  }
}