espace-paie-odentas/VIREMENTS_SALAIRES_AUTH_FIX.md

4.5 KiB

Fix - Authentification avec cookies

Problème identifié

Lors de la création d'un virement dans le modal, l'utilisateur recevait une alerte "Non authentifié".

Cause

Les composants et routes API utilisaient une méthode d'authentification incorrecte :

  • Composant client : Tentait de récupérer le token via supabase.auth.getSession() qui ne fonctionnait pas correctement dans le contexte
  • Routes API : Attendaient un header Authorization: Bearer <token> alors que l'application utilise des cookies httpOnly

Solution appliquée

1. Composant client (SalaryTransfersGrid.tsx)

Avant :

const { data: { session } } = await supabase.auth.getSession();
if (!session) {
  alert("Non authentifié");
  return;
}

const res = await fetch("/api/...", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${session.access_token}`,
  },
  body: JSON.stringify(data),
});

Après :

const res = await fetch("/api/...", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  credentials: "include", // Envoie automatiquement les cookies
  body: JSON.stringify(data),
});

2. Routes API

Avant :

import { createClient } from "@supabase/supabase-js";

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseServiceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY!;
const supabase = createClient(supabaseUrl, supabaseServiceRoleKey);

const authHeader = req.headers.get("authorization");
if (!authHeader || !authHeader.startsWith("Bearer ")) {
  return NextResponse.json({ error: "Missing or invalid Authorization header" }, { status: 401 });
}
const token = authHeader.replace("Bearer ", "");
const { data: { user }, error: authError } = await supabase.auth.getUser(token);

Après :

import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
import { cookies } from "next/headers";

const supabase = createRouteHandlerClient({ cookies });
const { data: { session }, error: sessionError } = await supabase.auth.getSession();

if (sessionError || !session) {
  return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const user = session.user;

3. Vérification Staff

Avant :

const { data: userData, error: userError } = await supabase
  .from("users")
  .select("is_staff")
  .eq("id", user.id)
  .single();
if (userError || !userData || !userData.is_staff) {
  return NextResponse.json({ error: "Forbidden: staff only" }, { status: 403 });
}

Après :

const { data: staffData, error: staffError } = await supabase
  .from("staff_users")
  .select("is_staff")
  .eq("user_id", user.id)
  .maybeSingle();
  
const isStaff = staffData?.is_staff || false;

if (!isStaff) {
  return NextResponse.json({ error: "Forbidden: staff only" }, { status: 403 });
}

Fichiers modifiés

  1. components/staff/SalaryTransfersGrid.tsx

    • Fonction handleCreateTransfer() : Ajout de credentials: "include", suppression de la récupération du token
    • Fonction handleGeneratePdf() : Ajout de credentials: "include", suppression de la récupération du token
  2. app/api/staff/virements-salaires/create/route.ts

    • Import de createRouteHandlerClient et cookies
    • Utilisation de getSession() au lieu de getUser(token)
    • Vérification dans la table staff_users au lieu de users
  3. app/api/staff/virements-salaires/generate-pdf/route.ts

    • Import de createRouteHandlerClient et cookies
    • Utilisation de getSession() au lieu de getUser(token)
    • Vérification dans la table staff_users au lieu de users

Conformité avec le reste du code

Cette approche est maintenant cohérente avec les autres routes API du projet, notamment :

  • app/api/informations/route.ts
  • app/api/tickets/route.ts
  • app/api/contrats/[id]/paies/route.ts

Tests à effectuer

  1. Créer un virement via le modal
  2. Générer un PDF pour un virement
  3. Vérifier que l'authentification fonctionne
  4. Vérifier que seuls les utilisateurs Staff peuvent accéder

Avantages de cette approche

  1. Sécurité : Les cookies httpOnly ne sont pas accessibles via JavaScript
  2. Simplicité : Pas besoin de gérer manuellement les tokens côté client
  3. Cohérence : Utilise la même méthode que le reste de l'application
  4. Automatique : Les cookies sont envoyés automatiquement avec credentials: "include"

Date du fix

13 octobre 2025