Home / Articles / Blog

Firebase, Supabase and Clerk Authentication
FirebaseSupabaseClerkAuthenticationNext.js

Firebase, Supabase and Clerk Authentication

How to add authentication using emaill, password using Firebase, Supabase and Clerk

Shrey Vijayvargiya

Under the Hood

Next.js is the best framework for building almost all categories of web applications. In today's section, we will again recall adding Authentication in a Next.js application.

The following options are available for adding Authentication:

  1. Firebase Authentication
  2. Supabase Authentication 
  3. Appwrite Authentication
  4. Authentication using Clerk

We have so many options to add authentication to the frontend website. Most of them help in session management, store management, OAuth user credentials and more.

But it's important to recall the options on the table before finalising the one of your choice.

Third-party Authentication

Every third-party authentication follows a step process along with a few lines of code execution to integrate authentication within the application.

For example, Firebase provides an admin dashboard for allowing each authentication provider from the options of Google, Github, Twitter, Slack, etc, which is then integrated within the application using a few lines of code.

This admin dashboard is the same for all kinds of third-party authentication such as Firebase, Supabase, Appwrite and Clerk.

Admin dashboards are quite common visual interaction websites to view and manage the authenticated users of each website. Since it's a dashboard by default, it will serve the purpose of multi-project management in itself. Each of the platforms, Firebase, Supabase and Appwrite or Clerk or any other in the future, will provide a dashboard for the same. Maybe in future we will have one interface to manage multiple authentication options from one single interface.

Initialization

Authentication or a serverless database usually needs initialisation at the time of starting the web server or running the client locally.

Firebase, Supabase, all need a way to initialise the npm module.

text
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";

const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "your-project.firebaseapp.com",
  projectId: "your-project-id",
  appId: "your-app-id",
};

const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);

Supabase

text
import { createClient } from '@supabase/supabase-js';

export const supabase = createClient(
  'https://your-project.supabase.co',
  'public-anon-key'
);

Clerk

text
import { Clerk } from '@clerk/clerk-sdk-node';

const clerk = new Clerk({ secretKey: 'YOUR_CLERK_SECRET_KEY' });

export default clerk;

Auth using Email and Signup

Firebase auth

text
import { createUserWithEmailAndPassword, signInWithEmailAndPassword } from 'firebase/auth';

export async function loginOrSignup(email: string, password: string, isSignup = false) {
  try {
    const userCredential = isSignup
      ? await createUserWithEmailAndPassword(auth, email, password)
      : await signInWithEmailAndPassword(auth, email, password);

    const user = userCredential.user;

    // Return important user credentials
    return {
      uid: user.uid,
      email: user.email,
      accessToken: await user.getIdToken(),
      provider: user.providerId,
      createdAt: user.metadata.creationTime,
      lastLogin: user.metadata.lastSignInTime,
    };
  } catch (error: any) {
    console.error("Auth Error:", error.code, error.message);
    throw error;
  }
}

Supabase auth

text
// supabaseAuth.ts
import { supabase } from './supabaseClient';

export async function loginOrSignupSupabase(email: string, password: string, isSignup = false) {
  const { data, error } = isSignup
    ? await supabase.auth.signUp({ email, password })
    : await supabase.auth.signInWithPassword({ email, password });

  if (error) throw error;

  const user = data.user;
  const session = data.session;

  return {
    uid: user?.id,
    email: user?.email,
    accessToken: session?.access_token,
    refreshToken: session?.refresh_token,
    createdAt: user?.created_at,
  };
}

Clerk auth

text
// clerkAuth.ts
import clerk from './clerkClient';

export async function loginOrSignupClerk(email: string, password: string, isSignup = false) {
  try {
    const user = isSignup
      ? await clerk.users.createUser({ emailAddress: [email], password })
      : await clerk.clients.verifyPassword({ identifier: email, password });

    const session = await clerk.sessions.createSession({
      userId: isSignup ? user.id : user.userId,
    });

    return {
      uid: session.userId,
      email,
      sessionId: session.id,
      createdAt: session.createdAt,
      lastActiveAt: session.lastActiveAt,
    };
  } catch (error: any) {
    console.error('Clerk Auth Error:', error);
    throw error;
  }
}

Quite an easy method to create a user account using email and password.

Moving ahead with new things to pay attention to, what about Google or Github authentication or even LinkedIn

Well, first and foremost step for each to enable the social login credentials needs an OAuth token or API key 

Google Auth API Key or Token: Access from the Google console account

Github Auth API Key or Token: Access it from Github OAuth credentials or API token by creating a Github account OAuth application

Twitter Auth API Key or Token: Access it from the X developer account, API key

Read more about each of them, as it's the most important and required method to enable social media login authentication using third-party or serverless.

Session Management

Some of the applications work on a session management concept, where each user, when authenticated within the application, has a session or a time range to access the application. The user is automatically logged out or can't access the application once the session is over.

A typical session management is handled mostly on the server side, but serverless applications do provide ways to manage sessions.

Firebase session management

text
import { auth } from './firebase';

const currentUser = auth.currentUser;
if (currentUser) {
  console.log('Logged in as:', currentUser.email);
}

Access token:

text
const token = await auth.currentUser?.getIdToken();

Firebase auth change

text
import { onAuthStateChanged } from 'firebase/auth';

onAuthStateChanged(auth, (user) => {
  if (user) {
    console.log('User is signed in:', user.uid);
  } else {
    console.log('User is signed out');
  }
});

Supabase session management

✅ Get Current Session/User

text
const {
  data: { session },
} = await supabase.auth.getSession();

const {
  data: { user },
} = await supabase.auth.getUser();

✅ Access Token

text
const accessToken = session?.access_token;

Auto Session Handling

Supabase automatically persists session in localStorage and refreshes tokens.

To listen for auth changes:

text
supabase.auth.onAuthStateChange((event, session) => {
  console.log('Auth event:', event);
  console.log('New session:', session);
});

✅ Logout

text
await supabase.auth.signOut();

Clerk session management

With Next.js Frontend SDK (@clerk/nextjs)

text
import { useUser, useAuth, useSession } from "@clerk/nextjs";

const { user } = useUser();
const { getToken } = useAuth();
const { session } = useSession();

const token = await getToken();

Logout (Frontend)

text
import { useClerk } from "@clerk/nextjs";

const { signOut } = useClerk();
signOut();

Backend Session (API route)

text
import { getAuth } from '@clerk/nextjs/server';

export default async function handler(req, res) {
  const { userId, sessionId, getToken } = getAuth(req);

  const token = await getToken();
  if (!userId) return res.status(401).json({ message: "Unauthorized" });

  res.status(200).json({ userId, sessionId, token });
}

Usually, we need session management only for a few categories of applications; other applications that mainly require session management can also be executed using browser storage as local storage or cookies.

Mainly, the categories of application, including heavy payments, large secured files, and user important credentials, need server-side session management; otherwise, cookies might work well.

User authenticated tokens are stored in cookies, which are then used as the token in the API headers to each of the serverless databases for verifying the user.

Firebase cookies management

Delete cookies

text
import Cookies from "js-cookie";
import { getAuth, signOut } from "firebase/auth";

export async function firebaseLogout() {
  await signOut(getAuth());
  Cookies.remove("fbToken");
}

Firebase verify cookies

text
import { NextApiRequest, NextApiResponse } from "next";
import { cert, getApps, initializeApp } from "firebase-admin/app";
import { getAuth as getAdminAuth } from "firebase-admin/auth";
import { parse } from "cookie";

if (!getApps().length) {
  initializeApp({ credential: cert(JSON.parse(process.env.FB_ADMIN_KEY!)) });
}

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const { fbToken } = parse(req.headers.cookie || "");

  if (!fbToken) return res.status(401).json({ message: "Missing token" });

  try {
    const decoded = await getAdminAuth().verifyIdToken(fbToken);
    return res.status(200).json({ uid: decoded.uid, email: decoded.email });
  } catch {
    return res.status(401).json({ message: "Invalid / expired token" });
  }
}

Supabase delete cookies

text
import Cookies from "js-cookie";
import { createClient } from "@supabase/supabase-js";

const supabase = createClient("https://…", "public-anon-key");

export async function supabaseLogout() {
  await supabase.auth.signOut();
  Cookies.remove("sbToken");
}

Supabase verify cookies

This is inside pages/api/verify-cookie.js in next <15 version and for next> 15 version prefer using pages/api/verify-cookie/route.ts

text
// pages/api/protected/supabase.ts
import { NextApiRequest, NextApiResponse } from "next";
import { createClient } from "@supabase/supabase-js";
import { parse } from "cookie";

const admin = createClient(
  "https://…",
  process.env.SUPABASE_SERVICE_ROLE_KEY!, // **service-role key only on server**
  { auth: { persistSession: false } }
);

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const { sbToken } = parse(req.headers.cookie || "");
  if (!sbToken) return res.status(401).json({ message: "Missing token" });

  const { data, error } = await admin.auth.getUser(sbToken);

  if (error || !data.user)
    return res.status(401).json({ message: "Invalid / expired token" });

  res.status(200).json({ uid: data.user.id, email: data.user.email });
}

Clerk verify cookies

text
import { NextApiRequest, NextApiResponse } from "next";
import { Clerk } from "@clerk/clerk-sdk-node";
import { parse } from "cookie";

const clerk = new Clerk({ secretKey: process.env.CLERK_SECRET_KEY! });

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const { ckToken } = parse(req.headers.cookie || "");
  if (!ckToken) return res.status(401).json({ message: "Missing token" });

  try {
    const payload = await clerk.verifyToken(ckToken);
    res.status(200).json({ uid: payload.sub, email: payload.email_address });
  } catch {
    res.status(401).json({ message: "Invalid / expired token" });
  }
}

Creating New Tokens

Most of the applications have shorter user session management, for example, 15 minutes of access once the user is logged in successfully. In that case, we usually prefer the way to recreate the user access token every time the user is asked to authenticate.

For that, we need a method to create new access tokens as shown below

Firebase create a new token

text
import { getAuth, onAuthStateChanged } from "firebase/auth";

const auth = getAuth();

onAuthStateChanged(auth, async (user) => {
  if (user) {
    // User is signed in, get their ID token
    try {
      const idToken = await user.getIdToken();
      console.log("Firebase ID Token:", idToken);
      // You can now send this token to your backend for verification
    } catch (error) {
      console.error("Error getting ID token:", error);
    }
  } else {
    // User is signed out
    console.log("No user signed in.");
  }
});

Supabase auth state changed to access the new token

text
supabase.auth.onAuthStateChanged((event, session) => {
  if (event === "SIGNED_IN" || event === "TOKEN_REFRESHED") {
    const user = session.user;
    const idToken = session.access_token; // or session.refresh_token, depending on your setup

    // Use user and idToken as needed
    console.log("User:", user);
    console.log("ID Token:", idToken);
  }
});

Clerk auth state changed to create a new token

text
// /api/clerk/refresh.ts
import { NextApiRequest, NextApiResponse } from "next";
import { clerkClient } from "@clerk/nextjs/server";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const sessionToken = req.cookies["__session"]; // Clerk httpOnly cookie

  if (!sessionToken) return res.status(401).json({ error: "No session token" });

  try {
    const session = await clerkClient.sessions.verifySession(sessionToken);

    const newToken = await clerkClient.sessions.createToken(session.id, {
      template: "your-jwt-template", // Optional, or omit for default
    });

    return res.status(200).json({ accessToken: newToken });
  } catch {
    return res.status(401).json({ error: "Invalid session" });
  }
}

It completely depends on the requirements of the project to use any of the above methods in your application.

The usual flow for each application remains the same as follows - 

  • User logged in using a third-party or email, password

  • The user provides access to use their credentials, such as email and password

  • The user is then provided with an access token that is stored in cookies 

  • User access to the website is provided or revoked depending on the access token verification 

Adding 3 YouTube videos for better reference, 

Embedded Content

Embedded Content

Embedded Content

That's it, see you in the next one

iHateReading

Summary

This document details Next.js authentication using Firebase, Supabase, Appwrite,and Clerk. Each employs third-party authentication via admin dashboards,requiring initialization using npm modules like firebase/app and @supabase/supabase-js. Email/password signup/login usescreateUserWithEmailAndPassword (Firebase), supabase.auth.signUp (Supabase), and Clerk's SDK. Session management involves accesstokens, onAuthStateChanged listeners (Firebase), and supabase.auth.onAuthStateChange (Supabase). Clerk utilizes its Next.js SDK for session handling and token creation. Cookie management andtoken refreshing are also discussed. Successful authentication relies on token verification.Thisdocument details Next.js authentication using Firebase, Supabase, Appwrite,and Clerk. Each employs third-party authentication via admin dashboards,requiring initialization using npm modules like firebase/app and @supabase/supabase-js. Email/password signup/login usescreateUserWithEmailAndPassword (Firebase), supabase.auth.signUp (Supabase), and Clerk's SDK. Session management involves accesstokens, onAuthStateChanged listeners (Firebase), and supabase.auth.onAuthStateChange (Supabase). Clerk utilizes its Next.js SDK for session handling and token creation. Cookie management andtoken refreshing are also discussed. Successful authentication relies on token verification.

External Links (4)

Explore topics

More in

Weekly letter

Subscribe to iHateReading

Our once-a-week newsletter on programming, jobs, AI, and building products online.

Our once a week newsletter on Programming, Jobs, AI, and Business