ZeroToShip

Docs/Feature

// feature

Login con Google

Il login con Google è già cablato in ZeroToShip e passa da Supabase Auth. Non c'è nessuna libreria OAuth di terze parti da installare: il bottone chiama signInWithOAuth, Supabase gestisce il giro su Google, e una route handler nel repo scambia il codice per una session. La parte di codice è già scritta — quello che devi fare tu è la configurazione su Google Cloud e su Supabase.

Il bottone in app/signin/page.tsx

La pagina /signin è un Client Component con una sola funzione, handleSignup, che gestisce sia OAuth che il magic link. Il bottone Google la chiama con type: “oauth” e provider: “google”:

app/signin/page.tsx
tsx
const supabase = createClient(); // libs/supabase/client.ts
const redirectURL = window.location.origin + "/api/auth/callback";

await supabase.auth.signInWithOAuth({
  provider: "google",
  options: {
    redirectTo: redirectURL,
  },
});

Nota il redirectTo: è sempre l'origin corrente più /api/auth/callback. In locale diventa http://localhost:3000/api/auth/callback, in produzione https://tuodominio.com/api/auth/callback. Entrambi vanno messi nella allowlist di Supabase (sotto).

Il callback che crea la session

Google rimanda l'utente a Supabase, Supabase rimanda al tuo /api/auth/callback con un ?code=... in query string. La route handler fa una cosa sola: scambia quel codice per una session e ti sposta sul callbackUrl del config.

app/api/auth/callback/route.ts
ts
export const dynamic = "force-dynamic";

export async function GET(req: NextRequest) {
  const requestUrl = new URL(req.url);
  const code = requestUrl.searchParams.get("code");

  if (code) {
    const supabase = await createClient();
    await supabase.auth.exchangeCodeForSession(code);
  }

  return NextResponse.redirect(requestUrl.origin + config.auth.callbackUrl);
}

Il callbackUrl di default è /dashboard — lo cambi in config.ts sotto auth. Attenzione a un dettaglio: la route ignora qualsiasi altro query param. components/ButtonGithubSignin.tsx passa un ?next= nell'URL di callback, ma nessuno lo legge: l'utente finisce comunque su config.auth.callbackUrl. Se ti serve tornare alla pagina di partenza, quel supporto va aggiunto a mano:

app/api/auth/callback/route.ts
ts
// Non è ancora nel repo: aggiungilo se ti serve il ritorno alla pagina di partenza
const next = requestUrl.searchParams.get("next");
const destination = next?.startsWith("/") ? next : config.auth.callbackUrl;

return NextResponse.redirect(requestUrl.origin + destination);

La session nel middleware

Dopo il login la session vive nei cookie. A tenerla fresca ci pensa il middleware in root: middleware.ts chiama updateSession di libs/supabase/middleware.ts, che ricrea un client server-side, legge e riscrive i cookie e fa un supabase.auth.getUser() a ogni richiesta — è quella chiamata che rinnova il token scaduto.

middleware.ts
ts
export async function middleware(request: NextRequest) {
  return await updateSession(request);
}

export const config = {
  matcher: [
    "/((?!_next/static|_next/image|favicon.ico|.*\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
  ],
};

Il matcher gira su tutto tranne asset statici e immagini. Non devi toccarlo per far funzionare Google — è già così.

Setup su Google Cloud

  1. Vai su console.cloud.google.com e crea un progetto (o usane uno esistente).
  2. APIs & Services → OAuth consent screen: tipo External, nome app, email di supporto, dominio. In modalità Testing solo gli account che aggiungi come test user possono entrare — quando sei pronto, pubblica.
  3. Credentials → Create Credentials → OAuth client ID, tipo Web application.
  4. Authorized JavaScript origins: http://localhost:3000 e https://tuodominio.com.
  5. Authorized redirect URIs: qui va l'URL di Supabase, non il tuo. La forma esatta è https://<project-ref>.supabase.co/auth/v1/callback. Il project-ref è la parte prima di .supabase.co nella tua NEXT_PUBLIC_SUPABASE_URL.
  6. Copia Client ID e Client Secret.

Errore numero uno di tutti: mettere http://localhost:3000/api/auth/callback tra i redirect URI di Google. Non è quello — Google parla con Supabase, Supabase parla con te.

Setup su Supabase

  1. Dashboard Supabase → Authentication → Providers → Google: attiva il toggle, incolla Client ID e Client Secret, salva.
  2. Authentication → URL Configuration: metti la Site URL (in dev http://localhost:3000, in produzione il tuo dominio) e aggiungi ai Redirect URLs entrambi gli indirizzi di callback: http://localhost:3000/api/auth/callback e https://tuodominio.com/api/auth/callback.
  3. Verifica che in .env.local ci siano NEXT_PUBLIC_SUPABASE_URL e NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY — sono le due chiavi lette da libs/supabase/client.ts. Dettagli in Variabili d'ambiente.

Se un redirect non è nella allowlist, Supabase ti sbatte sulla Site URL senza codice e sembra che il login “non faccia niente”. Novanta volte su cento il bug è lì.

Il bottone riutilizzabile

components/ButtonSignin.tsx non fa OAuth: legge l'utente con supabase.auth.getUser() e mostra l'avatar (da user_metadata.avatar_url, che Google popola da solo) con link a config.auth.callbackUrl, oppure un link a config.auth.loginUrl se non sei loggato. Mettilo nell'header e sei a posto.

Nel repo c'è un solo bottone OAuth stand-alone, ButtonGithubSignin.tsx. La versione Google non c'è: se ti serve fuori dalla pagina /signin, copia quel file e cambia il provider.

components/ButtonGoogleSignin.tsx
tsx
// Non è ancora nel repo: copia ButtonGithubSignin.tsx e cambia provider
await supabase.auth.signInWithOAuth({
  provider: "google",
  options: { redirectTo: window.location.origin + "/api/auth/callback" },
});

Come proteggere le pagine una volta che l'utente è dentro: Autenticazione.