S

Sajiron

Published on Jan 31, 2025

Implementing Firebase Admin SDK Authentication in Next.js

DALL·E 2025-01-31 23.34.50 - A futuristic Next.js web authentication interface showcasing Firebase Admin SDK integration. The design includes a login screen with a secure authenti.webp

Authenticating users in a Next.js application using Firebase Admin SDK allows for secure session handling with cookies, ensuring a smooth and scalable authentication mechanism. In this guide, we’ll explore how to implement Firebase authentication using the Admin SDK for server-side session management.

What We’ll Cover:

Setting up Firebase Admin SDK in Next.js

Implementing authentication with session cookies

Handling user authentication and authorization

Best practices for securing authentication

1. Setting Up Firebase Admin SDK in Next.js

Before implementing authentication, we need to configure Firebase Admin SDK in our Next.js project.

Install Firebase Admin SDK:

npm install firebase-admin

Configure Firebase Admin in server-app.ts:

This file initializes the Firebase Admin SDK and connects to Firestore, Firebase Auth, and Firebase Storage.

import 'server-only';
import { initializeApp, getApps, getApp, App, cert } from 'firebase-admin/app';
import { getAuth } from 'firebase-admin/auth';
import { getFirestore } from 'firebase-admin/firestore';
import { getStorage } from 'firebase-admin/storage';

let app: App;

if (getApps().length === 0) {
console.log('Initializing Firebase Admin App');
app = initializeApp({
credential: cert({
projectId: process.env.FIREBASE_ADMIN_PROJECT_ID,
clientEmail: process.env.FIREBASE_ADMIN_CLIENT_EMAIL,
privateKey: process.env.FIREBASE_ADMIN_PRIVATE_KEY.replace(/\\n/g, '\n'),
}),
databaseURL: process.env.FIRESTORE_DATABASE_URL,
storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
});
} else {
app = getApp();
}

export const adminFirestore = getFirestore(app);
export const adminAuth = getAuth(app);
export const adminStorage = getStorage(app);

Note: Make sure to configure your Firebase Admin credentials in .env.

2. Implementing Authentication in Next.js

2.1 Handling Authentication Requests (/auth)

In auth.ts, we create an endpoint to handle authentication requests by verifying Firebase tokens and setting secure session cookies.

import { cookies } from 'next/headers';
import { adminAuth } from '@/lib/firebase/server-app';
import { AUTH_TOKEN } from '@/constants/config';
import { Message } from '@/constants/messages';

export async function POST(request: Request) {
const authTokenHeader = request.headers.get('Authorization');

if (!authTokenHeader) {
return Response.json({ message: Message.AuthTokenMissing }, { status: 401 });
}

const [, token] = authTokenHeader.split('Bearer ');
const expiresIn = 7 * 24 * 60 * 60 * 1000;

return adminAuth
.createSessionCookie(token.trim(), { expiresIn })
.then(async sessionCookie => {
const requestCookies = await cookies();
requestCookies.set({
name: AUTH_TOKEN,
value: sessionCookie,
httpOnly: true,
sameSite: 'strict',
secure: true,
priority: 'high',
});

return adminAuth.verifySessionCookie(sessionCookie, true);
})
.then(claim => Response.json(claim))
.catch(() => Response.json({ message: Message.InvalidToken }, { status: 401 }));
}

Key Features:
✅ Uses Firebase ID token for authentication
✅ Creates a secure session cookie
✅ Ensures httpOnly and secure cookie settings

2.2 Checking Authentication Status (/auth/check)

In auth.ts, we create an endpoint to handle authentication requests by verifying Firebase tokens and setting secure session cookies.

import { Message } from '@/constants/messages';
import { authenticateRequest } from '@/lib/firebase/server-auth';

export async function GET() {
return authenticateRequest()
.then(claims => Response.json(claims))
.catch(() => Response.json({ message: Message.Unauthorised }, { status: 401 }));
}

Authentication Utility (server-auth.ts):

import 'server-only';
import { cookies } from 'next/headers';
import { adminAuth } from './server-app';
import { AUTH_TOKEN } from '@/constants/config';
import { Message } from '@/constants/messages';

export const authenticateRequest = async () => {
const requestCookies = await cookies();

try {
const token = requestCookies.get(AUTH_TOKEN);
if (!token) throw new Error(Message.AuthTokenMissing);
return adminAuth.verifySessionCookie(token.value, true);
} catch (error) {
requestCookies.delete(AUTH_TOKEN);
throw error;
}
};

Key Features:
✅ Reads authentication cookies
✅ Verifies session cookies with Firebase
✅ Handles unauthorized requests

3. Implementing Client-Side Authentication

The Firebase client SDK manages user authentication in the frontend.

3.1 Initializing Firebase in client-app.ts

import { initializeApp, getApps, getApp } from 'firebase/app';
import { getAuth, inMemoryPersistence, setPersistence } from 'firebase/auth';
import firebaseConfig from './config';

const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApp();
const auth = getAuth(app);
setPersistence(auth, inMemoryPersistence);

export { auth };

Why inMemoryPersistence?
It ensures Firebase Authentication state doesn’t persist across sessions, enhancing security.

3.2 Handling Authentication in client-auth.ts

import { signInWithEmailAndPassword as firebaseSignInWithEmailAndPassword } from 'firebase/auth';
import { auth } from './client-app';
import { API_ROUTE } from '@/constants/routes';

const sendAuthTokenForServerAuth = async (token: string) => {
const headers = new Headers();
headers.set('Authorization', `Bearer ${token}`);

return fetch(API_ROUTE.AUTH_AUTHENTICATE, {
method: 'POST',
headers,
}).then(res => res.json());
};

export const signInWithEmailAndPassword = async (email: string, password: string) => {
const userCredential = await firebaseSignInWithEmailAndPassword(auth, email, password);
const token = await userCredential.user.getIdToken();
return sendAuthTokenForServerAuth(token);
};

How It Works:
1️⃣ Firebase authenticates the user
2️⃣ Retrieves the ID token
3️⃣ Sends token to the server for validation
4️⃣ Server sets secure session cookies

4. Security Best Practices

Use httpOnly cookies: Prevents XSS attacks

Use sameSite: 'strict': Protects against CSRF attacks

Store private keys securely: Never hardcode them in code

Restrict Firebase API keys: Ensure API keys are domain-restricted

Conclusion

Implementing authentication with Firebase Admin SDK in Next.js allows for secure session-based authentication using cookies. We covered:

Firebase Admin setup

Secure authentication endpoints

Client-side authentication

Best practices for security

🚀 Next Steps

Implement user roles and permissions

Add OAuth sign-in with Google, Facebook, etc.

Improve session management with automatic refresh