When I built ONNS (옷늘날씨), I wanted sign-in to feel effortless. Nobody wants to manage yet another password, and I definitely did not want to spend weeks reinventing authentication. That is why I combined NextAuth with Supabase. Together, they allowed me to focus on building the fun parts of the app while still maintaining solid security.
Why This Combo?
- NextAuth is the de facto standard for authentication in Next.js apps. It supports OAuth providers, session management, JWTs, and integrates seamlessly with App Router.
- Supabase provides a complete Postgres backend with real-time and storage. Since user accounts and posts live in Supabase, tying authentication into the same ecosystem just made sense.
By combining them, I got the best of both worlds: flexible provider-based login through NextAuth, and user records managed in Supabase.
The Flow
- A user clicks Sign in with Google (or another provider).
- NextAuth handles the OAuth handshake.
- On success, NextAuth calls a custom adapter that syncs the user with Supabase.
- The session is then available across the app.
This way, authentication feels native, and all user-related data stays consistent in the database.
Example Implementation
NextAuth config:
// app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import { SupabaseAdapter } from "@next-auth/supabase-adapter";
export const authOptions = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
],
adapter: SupabaseAdapter({
url: process.env.SUPABASE_URL!,
secret: process.env.SUPABASE_SERVICE_ROLE_KEY!,
}),
secret: process.env.NEXTAUTH_SECRET,
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
Protecting a route:
// app/feed/page.tsx
import { getServerSession } from "next-auth";
import { authOptions } from "../api/auth/[...nextauth]/route";
export default async function FeedPage() {
const session = await getServerSession(authOptions);
if (!session) {
return <p>Please sign in to view the feed.</p>;
}
return <p>Welcome, {session.user?.name}! Here is your personalized feed.</p>;
}
Benefits I Saw
Simplicity: I did not need to roll my own login system. NextAuth handled the heavy lifting.
Security: Supabase stored user records in Postgres, and I could enforce RLS (row-level security) policies.
Flexibility: Adding more providers (GitHub, Twitter) is almost copy-paste.
Integration: Since posts, comments, and profiles already lived in Supabase, user identities aligned perfectly with the data model.
A Quick Lesson
NextAuth and Supabase both support authentication separately. You could use Supabase Auth alone. However, by using NextAuth, I achieved tighter integration with Next.js features, such as middleware and server actions. The adapter pattern meant I did not have to pick one or the other—I could blend them.
Final Thought
Authentication is rarely the most exciting part of a project, but it is the part that can ruin the experience if it goes wrong. By leveraging NextAuth and Supabase, ONNS achieved smooth user authentication and a maintainable setup for me.
How do you usually handle authentication in your Next.js projects?
Top comments (0)