Firebase JWT
Library
Firebase JWT
Overview
Firebase JWT is a JSON Web Token (JWT) based authentication system provided by Firebase Authentication service.
Details
Firebase JWT is an authentication system that uses JWT (JSON Web Token) format authentication tokens generated and managed by Firebase Authentication service. Through the Firebase JavaScript client SDK and Firebase Admin SDK, it provides a comprehensive authentication flow from client-side token acquisition to server-side token verification.
Firebase ID tokens are encrypted JWTs generated when users sign in to Firebase. These tokens contain basic profile information such as the user's UID, email address, and sign-in provider. The tokens are signed with Firebase's private key and can be verified using the Firebase Admin SDK or third-party JWT libraries.
Instead of Virtual DOM which represents virtual DOM elements, Firebase JWT uses a virtual token representation to minimize actual database access and achieve high security. Token-based authentication eliminates the need for session management, enabling stateless and scalable application development.
Currently, it has the ecosystem of Google Cloud, the world's largest cloud platform, and is used in combination with many related services such as Firebase Authentication, Firebase Firestore, Firebase Functions, and Firebase Hosting.
Pros and Cons
Pros
- Stateless Authentication: No need for session state management, enabling scalable architecture
- Automatic Token Renewal: SDK automatically manages and renews token expiration
- Multi-platform Support: Unified authentication experience across Web, iOS, Android, and Node.js
- Rich Authentication Providers: Integration with external authentication services like Google, Facebook, Twitter, GitHub
- Custom Claims: Ability to add application-specific permission information to tokens
- Firebase Ecosystem: Complete integration with Firestore, Functions, Hosting, etc.
- Development Efficiency: Significantly reduces authentication infrastructure construction and operation costs
Cons
- Firebase Dependency: Difficult to integrate with non-Firebase authentication systems
- Customization Limitations: Limited customization options for authentication flows
- Cost: Pay-per-use pricing for large user bases
- Vendor Lock-in: Difficult migration to other cloud providers
- Debugging Complexity: Sometimes difficult to identify the cause of token-related errors
Key Links
- Firebase Authentication Official Site
- Firebase JavaScript SDK
- Firebase Admin SDK
- Verify Firebase ID Tokens
- Firebase GitHub Repository
- Firebase Community
Usage Examples
Basic Sign-in and Token Acquisition
import { initializeApp } from 'firebase/app';
import { getAuth, signInWithEmailAndPassword } from 'firebase/auth';
// Initialize Firebase
const firebaseConfig = {
// configuration
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
// User sign-in
const signInUser = async (email, password) => {
try {
const userCredential = await signInWithEmailAndPassword(auth, email, password);
const user = userCredential.user;
// Get ID token
const idToken = await user.getIdToken();
console.log('ID Token:', idToken);
return user;
} catch (error) {
console.error('Sign-in error:', error);
throw error;
}
};
Force Token Refresh and Detailed Information Retrieval
import { getAuth } from 'firebase/auth';
const auth = getAuth();
// Get current user's token information
const getUserTokenInfo = async () => {
const user = auth.currentUser;
if (!user) {
throw new Error('User is not signed in');
}
try {
// Force token refresh
const idToken = await user.getIdToken(true);
// Get detailed token information
const idTokenResult = await user.getIdTokenResult();
console.log('ID Token:', idToken);
console.log('Issuer:', idTokenResult.claims.iss);
console.log('User ID:', idTokenResult.claims.sub);
console.log('Expiration:', new Date(idTokenResult.claims.exp * 1000));
console.log('Custom Claims:', idTokenResult.claims);
return {
token: idToken,
claims: idTokenResult.claims,
expirationTime: idTokenResult.expirationTime
};
} catch (error) {
console.error('Token acquisition error:', error);
throw error;
}
};
Server-side Token Verification (Node.js Admin SDK)
const admin = require('firebase-admin');
// Initialize Admin SDK
const serviceAccount = require('./path/to/serviceAccountKey.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
// Verify ID token
const verifyIdToken = async (idToken) => {
try {
// Verify and decode token
const decodedToken = await admin.auth().verifyIdToken(idToken);
console.log('User ID:', decodedToken.uid);
console.log('Email:', decodedToken.email);
console.log('Sign-in Provider:', decodedToken.firebase.sign_in_provider);
console.log('Auth Time:', new Date(decodedToken.auth_time * 1000));
return decodedToken;
} catch (error) {
console.error('Token verification error:', error);
throw new Error('Invalid token');
}
};
// Usage example in Express.js middleware
const authenticateToken = async (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // "Bearer TOKEN"
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
try {
const decodedToken = await verifyIdToken(token);
req.user = decodedToken;
next();
} catch (error) {
return res.status(403).json({ error: 'Invalid token' });
}
};
Custom Claims Setup and Usage
const admin = require('firebase-admin');
// Set custom claims (admin privileges)
const setAdminClaim = async (uid) => {
try {
await admin.auth().setCustomUserClaims(uid, {
admin: true,
role: 'administrator',
permissions: ['read', 'write', 'delete']
});
console.log(`Admin privileges granted to user ${uid}`);
} catch (error) {
console.error('Custom claim setting error:', error);
throw error;
}
};
// Check custom claims on client side
const checkUserRole = async () => {
const user = auth.currentUser;
if (!user) return null;
try {
// Force token refresh to get custom claims
const idTokenResult = await user.getIdTokenResult(true);
const isAdmin = idTokenResult.claims.admin || false;
const userRole = idTokenResult.claims.role || 'user';
const permissions = idTokenResult.claims.permissions || [];
return {
isAdmin,
role: userRole,
permissions
};
} catch (error) {
console.error('Role check error:', error);
return null;
}
};
Real-time Authentication State Monitoring
import { getAuth, onAuthStateChanged } from 'firebase/auth';
const auth = getAuth();
// Monitor authentication state
const setupAuthListener = () => {
return onAuthStateChanged(auth, async (user) => {
if (user) {
// User is signed in
console.log('User signed in:', user.uid);
try {
// Get latest token
const idToken = await user.getIdToken();
// Set in API request headers
setAuthHeader(idToken);
// Update user UI
updateUserUI(user);
} catch (error) {
console.error('Token acquisition error:', error);
}
} else {
// User is signed out
console.log('User signed out');
clearAuthHeader();
redirectToLogin();
}
});
};
// Set authentication header for API requests
const setAuthHeader = (token) => {
// For Axios
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
// For Fetch, set per request
window.firebaseToken = token;
};
// Sign out process
const signOutUser = async () => {
try {
await auth.signOut();
console.log('Sign out completed');
} catch (error) {
console.error('Sign out error:', error);
}
};
Authentication Check in Firebase Functions
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// Authentication check in HTTP function
exports.secureFunction = functions.https.onCall(async (data, context) => {
// Check authentication state
if (!context.auth) {
throw new functions.https.HttpsError(
'unauthenticated',
'Authentication required for this function'
);
}
const uid = context.auth.uid;
const email = context.auth.token.email;
// Check custom claims
const isAdmin = context.auth.token.admin === true;
if (!isAdmin) {
throw new functions.https.HttpsError(
'permission-denied',
'Administrator privileges required'
);
}
// Process for authenticated users only
try {
const result = await performAdminOperation(data, uid);
return { success: true, data: result };
} catch (error) {
throw new functions.https.HttpsError(
'internal',
'An error occurred during processing'
);
}
});