Skip to main content

Firebase Auth vs AWS Cognito vs Supabase Auth 2026

·PkgPulse Team

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 RLSauth.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

FeatureFirebase AuthAWS CognitoSupabase Auth
Setup complexityLowHighLow-Medium
Free tierUnlimited email/OAuth50k MAU50k MAU
Paid pricingGoogle Identity Platform$0.0055/MAU$25/month (Pro)
OAuth providers20+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.

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.