GreenOps : magic link Supabase, PKCE et déploiement Vercel

May 5, 2026

Contexte

GreenOps Console est une démo SaaS Next.js avec Supabase (PostgreSQL + RLS). La connexion repose sur un magic link par e-mail. En local, tout fonctionnait ; une fois déployé sur Vercel, le clic depuis la messagerie échouait (bad_code_verifier ou redirection vers /login?error=auth).

Ce n’est pas un problème de « mauvaise doc Supabase » : c’est un enchaînement SSR / cookies / PKCE qu’il vaut mieux comprendre pour un entretien full-stack.

Avec le flux PKCE, Supabase doit associer le code reçu dans l’URL du lien magique au code-verifier stocké dans le navigateur au moment où tu demandes le lien.

Si tu déclenches signInWithOtp dans une Server Action avec un client Supabase basé sur cookies() côté serveur, Next.js n’envoie pas toujours correctement les cookies au navigateur pour cette étape. Le serveur croit avoir posé le cookie ; en réalité le code-verifier n’est pas disponible au clic sur le mail → erreur bad_code_verifier côté API (POST /token).

Correction : initier signInWithOtp depuis le navigateur, avec createBrowserClient (@supabase/ssr), pour que le cookie soit bien attaché au domaine du site (ex. *.vercel.app).

Callback /auth/callback

Après le clic, Supabase redirige vers /auth/callback?code=.... Il faut échanger le code contre une session avec exchangeCodeForSession.

Si le client serveur utilise uniquement cookies() de Next sans rattacher les Set-Cookie à la réponse de redirection, la session peut ne pas être persistée. Il est plus fiable de créer le client avec :

  • lecture des cookies sur request.headers ;
  • écriture des cookies sur le NextResponse.redirect retourné (y compris les en-têtes anti-cache que @supabase/ssr peut passer dans setAll).

Variables d’environnement et Supabase

  • NEXT_PUBLIC_SITE_URL en production doit être l’URL publique du site (ex. https://…vercel.app), pas http://localhost:3000.
  • Dans Supabase → Authentication → URL Configuration :
    • Redirect URLs : inclure https://…/auth/callback ;
    • Site URL : alignée sur la prod pour éviter des comportements par défaut encore centrés sur le localhost.

Les variables NEXT_PUBLIC_* marquées Sensitive sur Vercel peuvent être absentes au build : pour une URL de démo publique, ce n’est généralement pas pertinent ; sur un portfolio, un fallback versionné dans le code évite aussi les surprises.

En résumé

| Sujet | Pistes utiles | |--------|----------------| | PKCE | signInWithOtp côté client (createBrowserClient) | | Session après redirect | Cookies sur NextResponse.redirect dans la Route Handler | | Config | NEXT_PUBLIC_SITE_URL, Redirect URLs et Site URL Supabase |

C’est exactement le type de problème qu’un profil full-stack Next.js + auth + prod est censé savoir isoler — indépendamment du métier énergie / climat.