Does Supabase Work With Auth.js?
You can use Supabase with Auth.js, but they're both full-featured auth solutions that require careful integration to avoid conflicts.
Quick Facts
How Supabase Works With Auth.js
Supabase and Auth.js are both complete authentication systems, which creates architectural tension. Supabase handles auth natively via PostgreSQL and its Auth service, while Auth.js provides abstracted auth logic across providers. The typical integration pattern is to use Auth.js as your session manager while delegating to Supabase as a custom provider or OAuth endpoint. You configure Auth.js with Supabase's OAuth credentials, letting Auth.js handle session state while Supabase manages user records. However, this means duplicating user data—Auth.js sessions don't automatically sync with Supabase's Auth tables. Alternatively, you can use Supabase Auth exclusively and skip Auth.js entirely, which is simpler. The hybrid approach works best when you need Auth.js's multi-provider orchestration (Google, GitHub, etc.) but want Supabase's PostgreSQL-backed user data and realtime capabilities. You'll need custom middleware to sync sessions and ensure both systems agree on user identity.
Best Use Cases
Auth.js with Supabase as Custom Provider
npm install next-auth @supabase/supabase-js// auth.ts (NextAuth configuration)
import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY);
export const { handlers, auth } = NextAuth({
providers: [
Credentials({
name: "Supabase",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
const { data, error } = await supabase.auth.signInWithPassword({
email: credentials.email as string,
password: credentials.password as string,
});
if (error || !data.user) return null;
return {
id: data.user.id,
email: data.user.email,
name: data.user.user_metadata?.name,
};
},
}),
],
pages: { signIn: "/login" },
});
export { handlers as GET, handlers as POST };Known Issues & Gotchas
Session desynchronization between Auth.js and Supabase Auth tables
Fix: Use Auth.js as your single session source-of-truth and either disable Supabase Auth or sync sessions via webhook/middleware. Don't rely on Supabase Auth tables for user identity if Auth.js is your primary auth system.
Supabase Auth service can revoke sessions independently, causing Auth.js to hold stale sessions
Fix: Implement session validation on each request by querying Supabase's auth status, or use Supabase Auth exclusively and bypass Auth.js for session management.
OAuth provider configuration conflicts when using both systems
Fix: Configure OAuth providers only in Auth.js (not Supabase), or use Supabase's OAuth exclusively and treat Auth.js as a thin wrapper around it.
RLS policies reference Supabase Auth tables, but Auth.js manages sessions separately
Fix: Use a custom claim or JWT that includes the user ID, or sync Auth.js user IDs into a parallel Supabase Auth record.
Alternatives
- •Supabase Auth alone + Next.js middleware (simplest, no extra auth layer needed)
- •Auth.js with a custom database adapter (Prisma + PostgreSQL) instead of Supabase
- •Clerk with Supabase (better integration, Clerk handles sessions, Supabase handles data)
Resources
Related Compatibility Guides
Explore more compatibility guides