Firebase Auth
Build with Firebase Authentication for user sign-in. Use this skill when the project needs to integrate Firebase Auth for email/password, Google, GitHub, Apple, phone OTP, anonymous auth, custom tokens, or ID token verification. Covers the Firebase Auth SDK (v9+), providers, auth state, custom claims, and server-side verification.
You are an auth specialist who integrates Firebase Authentication into projects.
Firebase Auth provides drop-in authentication with email/password, social
providers, phone auth, and anonymous auth — with client SDKs and server-side
token verification via the Admin SDK.
## Key Points
- Use `onAuthStateChanged` for reactive auth state — don't check `currentUser` directly on load
- Verify ID tokens server-side for API routes — never trust client-side auth alone
- Use custom claims for authorization (admin, plan, roles) — they're embedded in the token
- Force token refresh after setting custom claims — tokens are cached for up to 1 hour
- Use `signInWithRedirect` on mobile — popups are blocked on many mobile browsers
- Clean up auth state listeners in useEffect return functions
- Trusting `auth.currentUser` on page load — it's null until auth initializes
- Not verifying ID tokens server-side — client tokens can be spoofed
- Storing sensitive data in custom claims — they're visible in the JWT
- Using custom claims for frequently changing data — token refresh is needed
- Not handling the loading state — auth state takes a moment to initialize
- Creating custom session management when Firebase handles token refresh
## Quick Example
```bash
npm install firebase
npm install firebase-admin # Server-side
```
```typescript
import { signOut } from 'firebase/auth';
await signOut(auth);
```skilldb get auth-services-skills/firebase-authFull skill: 233 linesFirebase Authentication Integration
You are an auth specialist who integrates Firebase Authentication into projects. Firebase Auth provides drop-in authentication with email/password, social providers, phone auth, and anonymous auth — with client SDKs and server-side token verification via the Admin SDK.
Core Philosophy
Client-side first
Firebase Auth runs primarily on the client. The SDK handles the OAuth flows, token refresh, and auth state persistence. Your server verifies ID tokens but doesn't manage sessions — Firebase handles that automatically.
Provider-agnostic
Firebase Auth supports email/password, Google, Apple, GitHub, Twitter, Facebook, phone OTP, and anonymous auth through a consistent API. Adding a provider is enabling it in the console and adding a few lines of code.
Custom claims for authorization
Firebase Auth handles authentication (who is this user). For authorization (what
can they do), set custom claims on the user token — admin, plan, orgId —
and check them client-side or in security rules.
Setup
Install
npm install firebase
npm install firebase-admin # Server-side
Initialize (client)
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
const app = initializeApp({
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
});
export const auth = getAuth(app);
Initialize (server)
import { initializeApp, cert } from 'firebase-admin/app';
import { getAuth } from 'firebase-admin/auth';
const app = 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'),
}),
});
export const adminAuth = getAuth(app);
Key Techniques
Email/password auth
import { createUserWithEmailAndPassword, signInWithEmailAndPassword, updateProfile } from 'firebase/auth';
// Sign up
const { user } = await createUserWithEmailAndPassword(auth, email, password);
await updateProfile(user, { displayName: name });
// Sign in
const { user } = await signInWithEmailAndPassword(auth, email, password);
// Password reset
import { sendPasswordResetEmail } from 'firebase/auth';
await sendPasswordResetEmail(auth, email);
OAuth providers
import {
signInWithPopup, signInWithRedirect, getRedirectResult,
GoogleAuthProvider, GithubAuthProvider, OAuthProvider
} from 'firebase/auth';
// Google
const googleProvider = new GoogleAuthProvider();
const { user } = await signInWithPopup(auth, googleProvider);
// GitHub
const githubProvider = new GithubAuthProvider();
const { user } = await signInWithPopup(auth, githubProvider);
// Apple
const appleProvider = new OAuthProvider('apple.com');
appleProvider.addScope('email');
const { user } = await signInWithPopup(auth, appleProvider);
// Redirect flow (mobile-friendly)
await signInWithRedirect(auth, googleProvider);
// On page load:
const result = await getRedirectResult(auth);
if (result) {
const user = result.user;
}
Auth state listener
import { onAuthStateChanged, type User } from 'firebase/auth';
// React context pattern
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>
);
}
ID token verification (server-side)
// API route — verify Firebase ID token
export async function GET(req: Request) {
const authHeader = req.headers.get('authorization');
if (!authHeader?.startsWith('Bearer ')) {
return new Response('Unauthorized', { status: 401 });
}
const token = authHeader.split('Bearer ')[1];
try {
const decoded = await adminAuth.verifyIdToken(token);
const userId = decoded.uid;
const data = await db.query.posts.findMany({
where: eq(posts.authorId, userId),
});
return Response.json(data);
} catch {
return new Response('Invalid token', { status: 401 });
}
}
// Client-side — send token with requests
const token = await auth.currentUser?.getIdToken();
const res = await fetch('/api/posts', {
headers: { Authorization: `Bearer ${token}` },
});
Custom claims
// Server-side: set custom claims
await adminAuth.setCustomUserClaims(userId, {
admin: true,
plan: 'pro',
orgId: 'org_123',
});
// Client-side: read custom claims
const token = await auth.currentUser?.getIdTokenResult();
const isAdmin = token?.claims.admin === true;
const plan = token?.claims.plan as string;
// Force token refresh to get updated claims
await auth.currentUser?.getIdToken(true);
Phone auth
import { signInWithPhoneNumber, RecaptchaVerifier } from 'firebase/auth';
// Set up reCAPTCHA
const recaptcha = new RecaptchaVerifier(auth, 'recaptcha-container', {
size: 'invisible',
});
// Send verification code
const confirmationResult = await signInWithPhoneNumber(auth, '+1234567890', recaptcha);
// Verify code
const { user } = await confirmationResult.confirm(userEnteredCode);
Sign out
import { signOut } from 'firebase/auth';
await signOut(auth);
Best Practices
- Use
onAuthStateChangedfor reactive auth state — don't checkcurrentUserdirectly on load - Verify ID tokens server-side for API routes — never trust client-side auth alone
- Use custom claims for authorization (admin, plan, roles) — they're embedded in the token
- Force token refresh after setting custom claims — tokens are cached for up to 1 hour
- Use
signInWithRedirecton mobile — popups are blocked on many mobile browsers - Clean up auth state listeners in useEffect return functions
Anti-Patterns
- Trusting
auth.currentUseron page load — it's null until auth initializes - Not verifying ID tokens server-side — client tokens can be spoofed
- Storing sensitive data in custom claims — they're visible in the JWT
- Using custom claims for frequently changing data — token refresh is needed
- Not handling the loading state — auth state takes a moment to initialize
- Creating custom session management when Firebase handles token refresh
Install this skill directly: skilldb add auth-services-skills
Related Skills
Hanko
Integrate Hanko for modern, passwordless authentication in your web applications. Use Hanko to leverage passkeys (FIDO2), magic links, and email OTP for strong, developer-friendly auth experiences. This skill covers embedding Hanko's UI, handling authentication events, and server-side token verification.
Kinde
Build with Kinde for authentication and user management. Use this skill when the project needs to integrate Kinde for sign-up, sign-in, OAuth, feature flags, organizations, roles and permissions, or a developer-friendly auth platform with generous free tier. Covers the Kinde Next.js SDK, server/client auth, organizations, feature flags, and webhooks.
Logto
Logto is an open-source, managed identity service that simplifies authentication and authorization for your applications. Use Logto when you need a robust, scalable, and developer-friendly solution for user management, single sign-on (SSO), and API protection, supporting OIDC and OAuth 2.0.
Lucia
Build with Lucia for lightweight, self-hosted authentication. Use this skill when the project needs a minimal, dependency-free auth library for session management, OAuth, password hashing, and database-backed sessions without vendor lock-in. Covers Lucia v3, session management, OAuth via Arctic, password auth, and database adapters.
Nextauth
Build with NextAuth.js (Auth.js) for authentication in Next.js and other frameworks. Use this skill when the project needs self-hosted auth with OAuth providers, credentials, magic links, database sessions, JWT, callbacks, or middleware protection. Covers Auth.js v5, providers, adapters, callbacks, middleware, and session management.
Ory Kratos
Integrate Ory Kratos as a self-sovereign, API-first identity and user management system. Use this skill when you need a robust, scalable, and secure solution for user registration, login, profile management, and session handling in modern web applications, SPAs, or microservices, without building core identity flows from scratch.