From frontend-tools
Server-side Firebase Admin SDK patterns for Next.js 14+ with secure initialization and token verification. PROACTIVELY activate for: (1) setting up Admin SDK with server-only protection, (2) implementing custom claims for RBAC, (3) verifying tokens in middleware. Triggers: "admin sdk", "server-only", "custom claims"
How this skill is triggered — by the user, by Claude, or both
Slash command
/frontend-tools:firebase-admin-sdk-server-integrationThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The Firebase Admin SDK provides privileged server-side access to Firebase services, bypassing security rules. It must be used exclusively in server environments (Server Components, API Routes, Server Actions).
The Firebase Admin SDK provides privileged server-side access to Firebase services, bypassing security rules. It must be used exclusively in server environments (Server Components, API Routes, Server Actions).
MANDATORY: All Admin SDK files MUST import server-only to prevent client-side bundling.
// lib/firebase/admin.ts
import 'server-only'; // CRITICAL: First import, prevents client bundling
import { initializeApp, getApps, cert } from 'firebase-admin/app';
Why: Without server-only, service account credentials could leak into the client bundle, exposing your entire Firebase project.
// lib/firebase/admin.ts
import 'server-only';
import { initializeApp, getApps, cert, type App } from 'firebase-admin/app';
import { getAuth, type Auth } from 'firebase-admin/auth';
import { getFirestore, type Firestore } from 'firebase-admin/firestore';
import { getStorage, type Storage } from 'firebase-admin/storage';
/**
* Initialize Firebase Admin SDK with service account credentials
* ONLY use in server-side code
*/
const adminConfig = {
projectId: process.env.FIREBASE_ADMIN_PROJECT_ID,
credential: cert({
projectId: process.env.FIREBASE_ADMIN_PROJECT_ID,
clientEmail: process.env.FIREBASE_ADMIN_CLIENT_EMAIL,
// Firebase private keys contain \n characters that need to be unescaped
privateKey: process.env.FIREBASE_ADMIN_PRIVATE_KEY?.replace(/\\n/g, '\n'),
}),
};
// Validate environment variables
if (
!process.env.FIREBASE_ADMIN_PROJECT_ID ||
!process.env.FIREBASE_ADMIN_CLIENT_EMAIL ||
!process.env.FIREBASE_ADMIN_PRIVATE_KEY
) {
throw new Error(
'Missing Firebase Admin SDK environment variables. Ensure .env.local is configured.'
);
}
// Singleton pattern (prevent multiple initializations)
let adminApp: App;
if (getApps().length === 0) {
adminApp = initializeApp(adminConfig, 'admin');
} else {
adminApp = getApps()[0];
}
/**
* Admin Authentication
* Use for: token verification, custom claims, user management
*/
export const adminAuth: Auth = getAuth(adminApp);
/**
* Admin Firestore
* Use for: privileged data access, bypassing security rules
*/
export const adminDb: Firestore = getFirestore(adminApp);
/**
* Admin Storage
* Use for: signed URLs, server-side file operations
*/
export const adminStorage: Storage = getStorage(adminApp);
export { adminApp };
.env.local (Server-only variables):
# Firebase Admin SDK Credentials (NEVER commit these!)
FIREBASE_ADMIN_PROJECT_ID=your-project-id
FIREBASE_ADMIN_CLIENT_EMAIL=firebase-adminsdk-xxxxx@your-project-id.iam.gserviceaccount.com
FIREBASE_ADMIN_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nYourPrivateKeyHere\n-----END PRIVATE KEY-----\n"
Get Credentials:
.env.local// app/api/admin/set-role/route.ts
import 'server-only';
import { adminAuth } from '@/lib/firebase/admin';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
// Verify requester is admin
const token = request.cookies.get('firebase-token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const decodedToken = await adminAuth.verifyIdToken(token);
if (decodedToken.role !== 'admin') {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
// Set custom claim
const { uid, role } = await request.json();
await adminAuth.setCustomUserClaims(uid, { role });
return NextResponse.json({ success: true });
}
'use client';
import { auth } from '@/lib/firebase/client';
import { useEffect, useState } from 'react';
export function useUserRole() {
const [role, setRole] = useState<string | null>(null);
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(async (user) => {
if (user) {
const idTokenResult = await user.getIdTokenResult();
setRole(idTokenResult.claims.role as string || 'user');
} else {
setRole(null);
}
});
return unsubscribe;
}, []);
return role;
}
IMPORTANT: After setting custom claims, client must force token refresh:
// Client-side after role is changed
await auth.currentUser?.getIdToken(true); // Force refresh
Importing Admin SDK in Client Component:
'use client';
import { adminDb } from '@/lib/firebase/admin'; // ERROR: Leaks credentials!
Use Server Actions Instead:
// app/actions/getUser.ts
'use server';
import { adminDb } from '@/lib/firebase/admin';
export async function getUser(userId: string) {
const userDoc = await adminDb.collection('users').doc(userId).get();
return userDoc.data();
}
// Client component
'use client';
import { getUser } from '@/app/actions/getUser';
Do:
'server-only' first in admin filesDon't:
.env.local to version controlRelated Skills: firebase-authentication-patterns, firebase-nextjs-integration-strategies
npx claudepluginhub agentient/vibekit --plugin frontend-toolsGuides Firebase backend development covering auth, Firestore, Realtime DB, Cloud Functions, Storage, Hosting; emphasizes security rules, denormalized data modeling, and query optimization.
Build and configure Firebase-powered web and mobile apps: Firestore, Auth, Hosting, Cloud Functions, Storage, App Check, Remote Config, Analytics. Use for authentication flows, data modeling, hosting deployment, security rules.
Foundational Firebase CLI setup, authentication, and project management. Use for checking CLI version, initializing, authenticating, setting projects, and configuring google-services/GoogleService-Info files.