Next.js
How to Add Social Logins (Google & Facebook) to Next.js App with Supabase
Turn on Google and Facebook sign-in with Supabase Auth, start the OAuth flow from the browser, and finish it in a Next.js callback route.
- Next.js
- Supabase
- Auth
- OAuth
On this page
Supabase Auth already speaks OAuth. Your Next.js app starts the redirect, and a callback route trades the returned code for a session cookie. The examples below use the public anon key in the browser. Keep the service-role key on the server, and do not commit either value.
Turn the providers on
In the Supabase dashboard, open Authentication, then Providers. Enable Google and Facebook. Each provider asks for a client id and a client secret from that platform's developer console.
- Create a Google OAuth client and a Facebook app. Copy each client id and secret into the matching Supabase provider form.
- On Google and Facebook, set the allowed redirect to your project callback:
https://<project-ref>.supabase.co/auth/v1/callback. That address belongs to Supabase, not to your Next.js app. - In Supabase, under Authentication and URL configuration, add the addresses your app will return to:
http://localhost:3000/auth/callbackandhttps://your-domain.com/auth/callback.
Environment variables
Put the project URL and the anon key in .env.local. Both are safe to expose to the browser. The service-role key is not, so leave it out of any NEXT_PUBLIC_ variable.
NEXT_PUBLIC_SUPABASE_URL=https://<project-ref>.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=<your-anon-key>Start the OAuth redirect
signInWithOAuth sends the browser to Google or Facebook, then back to the redirectTo URL you pass. Use your own origin so local and production each return to the right callback.
"use client";
import { createBrowserClient } from "@supabase/ssr";
const supabase = createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
);
type Provider = "google" | "facebook";
export async function signInWith(provider: Provider) {
const redirectTo = `${window.location.origin}/auth/callback`;
const { error } = await supabase.auth.signInWithOAuth({
provider,
options: { redirectTo },
});
if (error) throw error;
}Exchange the code for a session
Supabase sends the user back to /auth/callback with a code query parameter. The route handler gives that code to exchangeCodeForSession, which writes the auth cookies. Only follow a next path that starts with /, so the parameter cannot redirect to another site.
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url);
const code = searchParams.get("code");
const next = searchParams.get("next") ?? "/";
const safeNext = next.startsWith("/") ? next : "/";
if (code) {
const cookieStore = await cookies();
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll();
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) => {
cookieStore.set(name, value, options);
});
},
},
},
);
const { error } = await supabase.auth.exchangeCodeForSession(code);
if (!error) {
return NextResponse.redirect(`${origin}${safeNext}`);
}
}
return NextResponse.redirect(`${origin}/login?error=auth`);
}What the callback is doing
- Read
codefrom the query string. Without it, there is no session to create. - Build a server client that can read and write cookies through
next/headers. - Call
exchangeCodeForSessiononce. Supabase sets the access and refresh cookies. - Redirect into the app. If the exchange fails, send the user to the login page with an error flag.
Login buttons
The buttons only call signInWith. Tailwind handles the layout. Google stays on a light button so the label remains readable. Facebook uses its blue. Both are type="button" so they do not submit a surrounding form.
"use client";
import { signInWith } from "@/lib/sign-in";
export function SocialLogin() {
return (
<div className="mx-auto flex w-full max-w-sm flex-col gap-3 rounded-2xl border border-white/10 bg-zinc-950 p-6">
<h2 className="text-lg font-semibold text-white">Continue</h2>
<button
type="button"
onClick={() => signInWith("google")}
className="rounded-xl bg-white px-4 py-2.5 text-sm font-medium text-zinc-950 transition-colors hover:bg-zinc-200"
>
Continue with Google
</button>
<button
type="button"
onClick={() => signInWith("facebook")}
className="rounded-xl bg-[#1877F2] px-4 py-2.5 text-sm font-medium text-white transition-colors hover:bg-[#1668d6]"
>
Continue with Facebook
</button>
</div>
);
}Check the round trip
- 01
Install the client
From the app, run
npm install @supabase/supabase-js @supabase/ssr. - 02
Click a provider
The browser should leave your site, land on Google or Facebook, and return to
/auth/callback. - 03
Confirm the session
After the redirect home,
supabase.auth.getUser()on the server should return the signed-in user. If it does not, the callback URL is missing from the Supabase allow list, or the provider secret does not match.
