Firebase Auth vs AWS Cognito vs Supabase Auth 2026
Firebase Auth vs AWS Cognito vs Supabase Auth 2026
TL;DR
Managed authentication services eliminate the complexity of building login systems — OAuth flows, token refresh, MFA, session management — but they come with very different DX, pricing models, and lock-in profiles. Firebase Authentication is the simplest to integrate, with SDKs for every platform, Google Sign-In working in minutes, and generous free tier; it's part of the Firebase/Google ecosystem and ideal for apps that already use Firestore or Cloud Functions. AWS Cognito is the enterprise-grade option — deeply integrated with IAM, API Gateway, and the broader AWS ecosystem; complex to configure but powerful for organizations already running infrastructure on AWS. Supabase Auth is the Postgres-native option — open-source, self-hostable, row-level security that integrates directly with Supabase's PostgreSQL, and a Next.js helper library with SSR support that works well with App Router. For Firebase/Google ecosystem: Firebase Auth. For AWS-native apps: Cognito. For Postgres/Supabase apps or self-hosting: Supabase Auth.
Key Takeaways
- Firebase free tier: 10k phone auth/month — email/OAuth are free unlimited
- Cognito free: 50,000 MAU — then $0.0055/MAU (cheapest managed auth at scale)
- Supabase Auth: 50,000 MAU free — same free tier as Cognito; open source self-hostable
- Firebase uses Google Identity Platform — UID-based, not JWT-customizable without Cloud Functions
- Cognito access tokens are JWTs — integrate directly with API Gateway authorizers
- Supabase Auth integrates with RLS —
auth.uid()function in PostgreSQL policies - All three support OAuth — Google, GitHub, Apple, Microsoft, etc.
Capability Quick Reference
Single sign-on (SSO/SAML) → Cognito (enterprise tier)
Google sign-in (1-click) → Firebase Auth (trivial setup)
Row-level security in Postgres → Supabase Auth
AWS API Gateway integration → Cognito (native JWT authorizer)
Self-hosted open source → Supabase Auth (or Lucia/Better Auth)
React Native support → All three (Firebase best SDK)
Phone/SMS auth → All three (Firebase easiest)
Magic link email → Supabase Auth, Firebase (via link)
Firebase Authentication
Firebase Auth is the Google-managed identity platform — supports email/password, phone, and 20+ OAuth providers with SDKs for Web, iOS, Android, and Unity.
Installation
npm install firebase
Initialize
// lib/firebase.ts
import { initializeApp, getApps } from "firebase/app";
import { getAuth } from "firebase/auth";
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY!,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN!,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID!,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET!,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID!,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID!,
};
const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0];
export const auth = getAuth(app);
Email and Password
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
sendPasswordResetEmail,
updateProfile,
onAuthStateChanged,
type User,
} from "firebase/auth";
import { auth } from "@/lib/firebase";
// Sign up
async function signUp(email: string, password: string, displayName: string): Promise<User> {
const credential = await createUserWithEmailAndPassword(auth, email, password);
await updateProfile(credential.user, { displayName });
return credential.user;
}
// Sign in
async function signIn(email: string, password: string): Promise<User> {
const credential = await signInWithEmailAndPassword(auth, email, password);
return credential.user;
}
// Sign out
async function logOut(): Promise<void> {
await signOut(auth);
}
// Reset password
async function resetPassword(email: string): Promise<void> {
await sendPasswordResetEmail(auth, email);
}
// Auth state listener
function onUserChange(callback: (user: User | null) => void) {
return onAuthStateChanged(auth, callback);
}
OAuth (Google, GitHub, Apple)
import {
GoogleAuthProvider,
GithubAuthProvider,
OAuthProvider,
signInWithPopup,
signInWithRedirect,
getRedirectResult,
type User,
} from "firebase/auth";
import { auth } from "@/lib/firebase";
const googleProvider = new GoogleAuthProvider();
googleProvider.addScope("email");
googleProvider.addScope("profile");
const githubProvider = new GithubAuthProvider();
githubProvider.addScope("user:email");
// Sign in with Google (popup)
async function signInWithGoogle(): Promise<User> {
const result = await signInWithPopup(auth, googleProvider);
return result.user;
}
// Sign in with GitHub
async function signInWithGitHub(): Promise<User> {
const result = await signInWithPopup(auth, githubProvider);
return result.user;
}
// Apple Sign-In
const appleProvider = new OAuthProvider("apple.com");
appleProvider.addScope("email");
appleProvider.addScope("name");
async function signInWithApple(): Promise<User> {
const result = await signInWithPopup(auth, appleProvider);
return result.user;
}
Server-Side Token Verification (Next.js)
// lib/firebase-admin.ts
import { initializeApp, getApps, cert } from "firebase-admin/app";
import { getAuth } from "firebase-admin/auth";
const adminApp =
getApps().length === 0
? initializeApp({
credential: cert({
projectId: process.env.FIREBASE_PROJECT_ID,
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, "\n"),
}),
})
: getApps()[0];
export const adminAuth = getAuth(adminApp);
// middleware.ts — verify Firebase ID token
import { NextRequest, NextResponse } from "next/server";
import { adminAuth } from "@/lib/firebase-admin";
export async function middleware(request: NextRequest) {
const token = request.cookies.get("firebaseToken")?.value;
if (!token) {
return NextResponse.redirect(new URL("/login", request.url));
}
try {
const decodedToken = await adminAuth.verifyIdToken(token);
// decodedToken.uid, .email, .name available
const response = NextResponse.next();
response.headers.set("x-user-id", decodedToken.uid);
return response;
} catch {
return NextResponse.redirect(new URL("/login", request.url));
}
}
Auth Context Hook
// contexts/AuthContext.tsx
"use client";
import { createContext, useContext, useEffect, useState } from "react";
import { onAuthStateChanged, type User } from "firebase/auth";
import { auth } from "@/lib/firebase";
interface AuthContextValue {
user: User | null;
loading: boolean;
}
const AuthContext = createContext<AuthContextValue>({ user: null, loading: true });
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setUser(user);
setLoading(false);
});
return unsubscribe;
}, []);
return (
<AuthContext.Provider value={{ user, loading }}>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => useContext(AuthContext);
AWS Cognito: Enterprise Auth
Cognito User Pools provide managed auth with JWTs that integrate natively with AWS API Gateway, Lambda authorizers, and IAM.
Installation
npm install aws-amplify @aws-amplify/auth
# Or for direct Cognito SDK:
npm install @aws-sdk/client-cognito-identity-provider
Amplify Setup
// lib/amplify.ts
import { Amplify } from "aws-amplify";
Amplify.configure({
Auth: {
Cognito: {
userPoolId: process.env.NEXT_PUBLIC_COGNITO_USER_POOL_ID!,
userPoolClientId: process.env.NEXT_PUBLIC_COGNITO_CLIENT_ID!,
signUpVerificationMethod: "code",
loginWith: {
oauth: {
domain: process.env.NEXT_PUBLIC_COGNITO_DOMAIN!,
scopes: ["email", "openid", "profile"],
redirectSignIn: [process.env.NEXT_PUBLIC_APP_URL + "/callback"],
redirectSignOut: [process.env.NEXT_PUBLIC_APP_URL],
responseType: "code",
},
},
},
},
});
Sign Up and Sign In
import {
signUp,
confirmSignUp,
signIn,
signOut,
getCurrentUser,
fetchAuthSession,
resetPassword,
confirmResetPassword,
} from "aws-amplify/auth";
// Sign up
async function registerUser(email: string, password: string, name: string) {
const { isSignUpComplete, userId, nextStep } = await signUp({
username: email,
password,
options: {
userAttributes: {
email,
name,
},
},
});
return { isSignUpComplete, userId, nextStep };
}
// Confirm sign up with verification code
async function confirmRegistration(email: string, code: string) {
const { isSignUpComplete } = await confirmSignUp({
username: email,
confirmationCode: code,
});
return isSignUpComplete;
}
// Sign in
async function loginUser(email: string, password: string) {
const { isSignedIn, nextStep } = await signIn({ username: email, password });
// Handle MFA or custom challenges
if (nextStep.signInStep === "CONFIRM_SIGN_IN_WITH_TOTP_CODE") {
return { requiresMFA: true };
}
return { isSignedIn };
}
// Get JWT tokens for API calls
async function getAccessToken(): Promise<string> {
const session = await fetchAuthSession();
return session.tokens?.accessToken.toString() ?? "";
}
OAuth with Cognito Hosted UI
import { signInWithRedirect, signOut } from "aws-amplify/auth";
// Redirect to Cognito hosted UI for Google login
async function signInWithGoogle() {
await signInWithRedirect({
provider: "Google",
});
// After redirect, user lands at redirectSignIn URL
// Call getCurrentUser() to get the authenticated user
}
// Sign in with GitHub (custom OIDC provider configured in Cognito)
async function signInWithGitHub() {
await signInWithRedirect({
provider: { custom: "GitHub" },
});
}
Verify Token Server-Side
// lib/cognito-verifier.ts
import { CognitoJwtVerifier } from "aws-jwt-verify";
const verifier = CognitoJwtVerifier.create({
userPoolId: process.env.COGNITO_USER_POOL_ID!,
tokenUse: "access",
clientId: process.env.COGNITO_CLIENT_ID!,
});
export async function verifyCognitoToken(token: string) {
try {
const payload = await verifier.verify(token);
return payload; // payload.sub = user ID, payload.username, payload["cognito:groups"]
} catch {
return null;
}
}
// middleware.ts
import { verifyCognitoToken } from "@/lib/cognito-verifier";
export async function middleware(request: NextRequest) {
const token = request.headers.get("Authorization")?.replace("Bearer ", "");
if (!token) return NextResponse.redirect(new URL("/login", request.url));
const payload = await verifyCognitoToken(token);
if (!payload) return NextResponse.redirect(new URL("/login", request.url));
return NextResponse.next();
}
Supabase Auth: Postgres-Native Auth
Supabase Auth provides authentication that integrates directly with PostgreSQL row-level security — auth.uid() in SQL policies references the authenticated user.
Installation
npm install @supabase/supabase-js @supabase/ssr
Setup (Next.js App Router)
// utils/supabase/client.ts
import { createBrowserClient } from "@supabase/ssr";
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
}
// utils/supabase/server.ts
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";
export async function createClient() {
const cookieStore = await cookies();
return 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);
});
},
},
}
);
}
Email/Password Auth
// Client component
import { createClient } from "@/utils/supabase/client";
const supabase = createClient();
// Sign up
async function signUp(email: string, password: string) {
const { data, error } = await supabase.auth.signUp({
email,
password,
options: {
data: {
full_name: "John Doe", // Stored in auth.users.raw_user_meta_data
},
},
});
if (error) throw error;
return data.user;
}
// Sign in
async function signIn(email: string, password: string) {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) throw error;
return data.user;
}
// Sign out
async function logOut() {
await supabase.auth.signOut();
}
OAuth
// Sign in with GitHub
async function signInWithGitHub() {
const { error } = await supabase.auth.signInWithOAuth({
provider: "github",
options: {
redirectTo: `${process.env.NEXT_PUBLIC_APP_URL}/auth/callback`,
scopes: "read:user user:email",
},
});
if (error) throw error;
}
// Sign in with Google
async function signInWithGoogle() {
const { error } = await supabase.auth.signInWithOAuth({
provider: "google",
options: {
redirectTo: `${process.env.NEXT_PUBLIC_APP_URL}/auth/callback`,
},
});
if (error) throw error;
}
// Magic link (passwordless)
async function sendMagicLink(email: string) {
const { error } = await supabase.auth.signInWithOtp({
email,
options: {
emailRedirectTo: `${process.env.NEXT_PUBLIC_APP_URL}/auth/callback`,
},
});
if (error) throw error;
}
OAuth Callback Route
// app/auth/callback/route.ts
import { createClient } from "@/utils/supabase/server";
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
const { searchParams, origin } = new URL(request.url);
const code = searchParams.get("code");
const next = searchParams.get("next") ?? "/dashboard";
if (code) {
const supabase = await createClient();
const { error } = await supabase.auth.exchangeCodeForSession(code);
if (!error) {
return NextResponse.redirect(`${origin}${next}`);
}
}
return NextResponse.redirect(`${origin}/auth/error`);
}
Server Component Session Check
// app/dashboard/page.tsx
import { createClient } from "@/utils/supabase/server";
import { redirect } from "next/navigation";
export default async function DashboardPage() {
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
redirect("/login");
}
// Fetch user's data — RLS automatically filters by auth.uid()
const { data: posts } = await supabase
.from("posts")
.select("*")
.order("created_at", { ascending: false });
return <Dashboard user={user} posts={posts ?? []} />;
}
Row-Level Security Integration
-- SQL: Enable RLS with Supabase Auth
CREATE TABLE posts (
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
user_id uuid REFERENCES auth.users(id) NOT NULL,
title text NOT NULL,
content text,
created_at timestamptz DEFAULT now()
);
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Users can only see their own posts
CREATE POLICY "Users see own posts"
ON posts FOR SELECT
USING (auth.uid() = user_id);
-- Users can only insert their own posts
CREATE POLICY "Users insert own posts"
ON posts FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- Users can only update their own posts
CREATE POLICY "Users update own posts"
ON posts FOR UPDATE
USING (auth.uid() = user_id);
Feature Comparison
| Feature | Firebase Auth | AWS Cognito | Supabase Auth |
|---|---|---|---|
| Setup complexity | Low | High | Low-Medium |
| Free tier | Unlimited email/OAuth | 50k MAU | 50k MAU |
| Paid pricing | Google Identity Platform | $0.0055/MAU | $25/month (Pro) |
| OAuth providers | 20+ | 20+ (with OIDC) | 20+ |
| Next.js SSR | ⚠️ (needs firebase-admin) | ✅ (JWT verify) | ✅ (first-class) |
| App Router | ⚠️ Manual setup | ⚠️ Amplify SSR | ✅ @supabase/ssr |
| Postgres RLS | ❌ | ❌ | ✅ Native |
| Self-hostable | ❌ | ❌ | ✅ |
| MFA | ✅ TOTP + SMS | ✅ TOTP + SMS | ✅ TOTP |
| Phone/SMS auth | ✅ | ✅ | ✅ |
| Magic link | ✅ (email link) | ❌ | ✅ |
| SAML/SSO | ✅ (GCIP) | ✅ | ✅ |
| AWS integration | ❌ | ✅ Native | ❌ |
| React Native | ✅ Excellent SDK | ✅ Amplify | ✅ |
When to Use Each
Choose Firebase Auth if:
- Already using Firebase (Firestore, Cloud Functions, Storage) — same project, same SDK
- Google Sign-In in under 10 minutes is appealing (trivial OAuth setup)
- Mobile-first: excellent React Native and native iOS/Android SDKs
- Phone/SMS authentication is a primary requirement
- Google's infrastructure and reliability matter
Choose AWS Cognito if:
- Your backend runs on AWS (API Gateway, Lambda, AppSync) — native JWT authorizer integration
- SAML/SSO for enterprise customers is required
- Fine-grained IAM-based authorization on AWS resources
- 50k+ MAU where Cognito's pricing is competitive
- Organization has existing AWS expertise and infrastructure
Choose Supabase Auth if:
- Using Supabase's PostgreSQL for your database — RLS with
auth.uid()is a killer feature - Next.js App Router with first-class SSR support (
@supabase/ssr) - Open-source and self-hosting is important (vendor lock-in concern)
- Magic link / passwordless email is a core auth method
- Row-level security eliminates entire categories of authorization code
Methodology
Data sourced from Firebase Authentication documentation (firebase.google.com/docs/auth), AWS Cognito documentation (docs.aws.amazon.com/cognito), Supabase Auth documentation (supabase.com/docs/guides/auth), pricing pages as of February 2026, npm download statistics, and community discussions from the Firebase Google Group, AWS re:Post, and Supabase Discord.
Related: Lucia Auth v3 vs Better Auth vs Stack Auth for self-hosted, code-owned authentication alternatives, or Prisma vs Drizzle vs Kysely for the database ORMs that work alongside these auth systems.