Skip to main content

SSO with Firebase Authentication

· 12 min read
ShinaBR2

About this article

As a frontend developer, I've witnessed a transformation in how we build web applications. This article shares my journey with Firebase, exploring how it challenges traditional development paradigms and why its approach might be exactly what many projects need. Whether you're considering Firebase for your next project or curious about modern web architecture, you'll discover how Firebase's philosophy can reshape your development workflow.

Why I Love Firebase: A Frontend Developer's Perspective

The Promise of Simplicity

I love Firebase and its ecosystem. It fundamentally changed how I think about web development by challenging traditional architectures. What makes Firebase special? It starts with its incredible simplicity: you can build a fully functional application in minutes. The platform offers real-time updates by default, preview channels for testing frontend changes before deployment, and seamless authentication. But what truly sets Firebase apart is its revolutionary mindset – allowing client-side code to interact directly with the database.

This direct interaction might sound controversial to developers steeped in traditional architectures, but it represents a paradigm shift in how we build modern web applications. Let me explain why this excites me so much.

Challenging Traditional Architecture

The traditional web development world often draws a strict line between frontend and backend development. In this conventional view, backend development takes center stage, handling most of the business logic, data validation, and security. Frontend development, by contrast, is sometimes dismissed as merely dealing with styles, layouts, and user interface concerns. This perspective suggests that real complexity lives exclusively on the server side.

I fundamentally disagree with this limited view. Modern frontend development has evolved into something far more sophisticated – essentially becoming a full-stack discipline in its own right. Firebase recognizes and embraces this evolution, providing tools that empower frontend developers to build complete, secure applications without getting bogged down in backend infrastructure.

Firebase's architecture liberates frontend developers from many traditional backend concerns. Instead of wrestling with servers, reverse proxies, API gateways, and load balancers, developers can focus on building features that matter to their users. This shift raises an important question: Do most applications really need the complexity of a traditional backend?

Let's be honest about a truth many developers don't openly discuss: for most applications, especially in their early stages, we don't need to handle millions of active users or requests per day. Often, the most frequent visitor to our system is our own test account. Traditional architecture can be overengineering for many use cases, introducing complexity before it's actually needed.

Real-Time Made Simple

Firebase's real-time capabilities perfectly exemplify this simplified yet powerful approach. Consider a traditional setup: to implement real-time updates, you'd need to manage WebSocket connections, handle connection states, implement reconnection logic, and manually synchronize data between server and client. But why manage all this complexity yourself?

With Firebase's Realtime Database or Firestore, real-time updates become remarkably straightforward. The system aligns beautifully with React's core principle of state management: when data changes, your UI updates automatically. Here's a practical example:

// Traditional WebSocket approach
class DataManager {
constructor() {
this.socket = new WebSocket('ws://your-server/data');
this.socket.onmessage = this.handleMessage;
this.socket.onclose = this.handleReconnection;
// Add more connection management logic...
}

handleMessage(event) {
const data = JSON.parse(event.data);
// Complex logic to update local state
// Manage consistency between server and client
}
}

// Firebase approach
import { collection, onSnapshot } from 'firebase/firestore';

function DataComponent() {
useEffect(() => {
const unsubscribe = onSnapshot(collection(db, 'items'), snapshot => {
// Data automatically stays in sync
setItems(snapshot.docs.map(doc => doc.data()));
});
return () => unsubscribe();
}, []);
}

This comparison reveals how Firebase eliminates boilerplate code and complex infrastructure management. The real-time functionality isn't just an add-on feature — it's woven into Firebase's DNA. You don't need to think about implementing WebSocket servers, managing connection states, or handling data synchronization. Instead, you can focus on building features that provide value to your users.

Some developers argue that we always need a separate backend server, but when pressed, they often struggle to justify this position with concrete requirements. Many times, this insistence stems from familiarity with traditional architectures rather than actual project needs. Firebase challenges this conventional wisdom by providing robust security rules that protect your data without requiring an intermediate server layer.

Think of Firebase's security rules as your backend validation logic in a declarative form. Instead of writing and maintaining separate validation code on a backend server, you define your security and validation requirements right where they matter—close to your data. This approach isn't just simpler; it's often more maintainable and easier to reason about.

Data Ownership: Empowering Frontend Teams

Firebase's approach to data structure brings us to another powerful paradigm shift in modern web development. Just as GraphQL empowers frontend developers to request exactly the data they need, Firebase lets frontend teams define data structures that perfectly match their UI requirements. This alignment between data and UI brings significant advantages to the development process.

Think about the traditional workflow: frontend developers often wait for backend teams to define API endpoints and data structures. They might receive data in formats that don't quite match their UI needs, leading to unnecessary data transformation layers. Sometimes, debates arise over whether field names should use camelCase or snake_case, creating friction that doesn't add value to the end user's experience.

But who better understands the data requirements of a UI than the developers building that UI? When working with Firebase, frontend developers can take ownership of their data structures, designing them to support their UI components efficiently. Here's a practical example:

// Traditional API response requiring transformation
const apiResponse = {
user_profile: {
first_name: 'John',
last_name: 'Doe',
profile_image_url: 'https://...',
last_login_timestamp: '2024-01-01T00:00:00Z',
},
};

// Transform for frontend use
const transformedData = {
userProfile: {
fullName: `${apiResponse.user_profile.first_name} ${apiResponse.user_profile.last_name}`,
avatarUrl: apiResponse.user_profile.profile_image_url,
lastSeen: new Date(apiResponse.user_profile.last_login_timestamp),
},
};

// With Firebase, structure your data as needed from the start
const userProfileRef = collection(db, 'userProfiles');
await setDoc(doc(userProfileRef), {
fullName: 'John Doe',
avatarUrl: 'https://...',
lastSeen: serverTimestamp(),
// Structure matches UI requirements exactly
});

This direct control over data structure yields several benefits:

  • Reduced complexity in frontend code by eliminating transformation layers
  • Faster development cycles without cross-team dependencies
  • Better performance by storing data in a format that matches UI consumption patterns
  • Clearer ownership and responsibility boundaries within teams

However, this flexibility comes with responsibility. Just as GraphQL requires thoughtful schema design, Firebase projects need careful planning of data structures and security rules. You'll want to consider:

  • Data access patterns and query efficiency
  • Security rule implications of your structure
  • Data normalization vs. denormalization tradeoffs
  • Real-time update performance

The JavaScript ecosystem, despite its rapid evolution and occasional fragmentation (yes, we still can't agree on the perfect bundler in 2024!), provides fertile ground for this frontend-centric development approach. The ecosystem's flexibility allows teams to adapt quickly to changing requirements while maintaining a robust and secure application architecture.

The Security Rules Paradigm

Let's address a common concern: 'What about data consistency and validation?' Firebase's security rules provide a robust answer. These rules act as a declarative backend, ensuring data integrity and access control without requiring a traditional API layer. Consider this example:

// Firebase security rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /userProfiles/{userId} {
allow read: if request.auth != null;
allow write: if request.auth.uid == userId &&
request.resource.data.fullName is string &&
request.resource.data.fullName.size() > 0;
}
}
}

These rules provide server-side validation while maintaining the simplicity of client-side data management. They're not just access controls; they're a complete validation layer that runs before any data modification occurs.

The SSO Challenge: When Simplicity Meets Enterprise

Like any technology choice, Firebase comes with its own set of challenges and limitations. Understanding these helps us make informed decisions about when to use Firebase and when to consider alternatives. Let me share a particularly challenging limitation I've encountered: implementing Single Sign-On (SSO) with Firebase Authentication.

Consider this seemingly straightforward requirement: users sign in at app1.mydomain.com and should automatically be authenticated when they navigate to app2.mydomain.com. This is a common enterprise requirement, exemplified by how Google's authentication works across its services like Google Search and YouTube. However, implementing this pattern with Firebase presents some interesting challenges.

To understand why this is complex, let's first break down how SSO typically works:

// Traditional SSO Flow
// 1. User signs in at app1.mydomain.com
const response = await authProvider.signIn(credentials);
// Server sets a shared cookie for *.mydomain.com
document.cookie = 'SESSION_ID=abc123; domain=.mydomain.com';

// 2. User navigates to app2.mydomain.com
// Server automatically reads the shared cookie
// and establishes the session

// With Firebase Authentication
// Each application maintains its own session
const auth = getAuth();
await signInWithEmailAndPassword(auth, email, password);
// No built-in way to share this session across domains

The key components of traditional SSO are:

  • A shared cookie domain that works across your applications
  • A central authentication server that manages sessions
  • A mechanism for applications to validate the shared session

Firebase Authentication, however, was designed with a different philosophy. It provides excellent authentication services for individual applications but wasn't built to be an identity provider. It handles the fundamental authentication flow—validating credentials and providing authentication tokens—but doesn't manage cross-domain sessions out of the box.

You might wonder, "Can't we just build this ourselves?" The answer is yes, but it requires significant additional infrastructure:

// Custom SSO implementation with Firebase
// 1. Create a central authentication service
const centralAuth = {
async validateSession(sessionToken) {
// Verify the shared cookie
// Check session validity
// Generate Firebase custom token
const firebaseToken = await admin.auth().createCustomToken(userId);
return firebaseToken;
},
};

// 2. Client-side implementation
async function handleCrossDomainAuth() {
if (hasValidSharedCookie()) {
const firebaseToken = await centralAuth.validateSession(
getCookie('SESSION_ID')
);
await signInWithCustomToken(auth, firebaseToken);
}
}

This solution works, but it introduces exactly what we were trying to avoid with Firebase: a custom backend server. We now need to:

  • Maintain a session management service
  • Handle cookie security across domains
  • Implement token generation and validation
  • Deploy and scale this infrastructure

You might think, "Let's use an established SSO provider like Auth0 instead!" While this solves the SSO challenge, it fundamentally changes how we interact with Firebase. Now every request must first go through the SSO provider's servers, effectively negating one of Firebase's key benefits: direct client-to-database communication.

The security rules we carefully crafted become less relevant because we're now managing authorization at the SSO provider level. Our elegant client-side data management patterns need to be rearchitected. The development simplicity we cherished begins to erode.

This presents a crucial decision point for development teams. If your application absolutely requires SSO, especially in an enterprise context, you should carefully consider whether Firebase remains the right choice. The additional complexity of implementing SSO might outweigh Firebase's benefits of simplicity and direct data access.

There are other workarounds, like using Firebase's custom authentication tokens or implementing a hybrid approach, but each comes with its own complexity and trade-offs. In my experience, if SSO is a core requirement from the start, it's often better to:

  • Choose a traditional architecture with built-in SSO support
  • Use an identity provider that's designed for enterprise SSO scenarios
  • Accept the additional complexity of server-side components from the beginning

Remember, this isn't a criticism of Firebase—it's about understanding its sweet spot. Firebase excels in many scenarios, but enterprise SSO isn't one of them. Making this trade-off explicit early in your project can save significant development effort later.

Conclusion

The journey through Firebase's capabilities and limitations reveals a broader truth about modern web development: there's no one-size-fits-all solution. Firebase represents a paradigm shift in how we think about building web applications, challenging traditional assumptions about the necessity of complex backend infrastructure.

Firebase's true power lies not just in its technical capabilities, but in how it reimagines the development workflow. By allowing frontend developers to work directly with data, implement real-time features effortlessly, and manage security through declarative rules, Firebase eliminates many of the friction points that traditionally slow down development.

This approach particularly shines in scenarios where you need to:

  • Rapidly prototype and validate ideas
  • Build real-time collaborative features
  • Deploy full-stack applications with minimal infrastructure management
  • Empower frontend teams to own the entire product development cycle

However, my exploration of SSO implementation challenges teaches me something valuable about technological choices: understanding limitations is just as important as appreciating capabilities. Firebase's simplicity comes with trade-offs, and some enterprise requirements might push you toward more traditional architectures.

Looking ahead, the web development landscape continues to evolve. While we might still debate about the best bundler in 2024, the trend toward empowering frontend developers with full-stack capabilities is clear. Firebase pioneered this approach, showing us that frontend development has grown far beyond its traditional boundaries.

For teams considering Firebase, I recommend asking these key questions:

  • Do you need enterprise features like SSO or complex authentication flows?
  • What is your expected scale and data complexity?
  • How important is real-time functionality to your application?
  • Does your team have the expertise to properly structure data and security rules?

The answers to these questions shouldn't necessarily determine whether you use Firebase, but rather how you use it. You might choose Firebase for certain components of your application while maintaining traditional infrastructure for others. The key is understanding where Firebase's strengths align with your specific needs.

My experience with Firebase has fundamentally changed how I think about web development. It's shown me that many of our assumed complexities in web architecture aren't always necessary. While Firebase isn't perfect—as the ongoing monorepo deployment challenge shows—its approach to simplifying web development while maintaining security and scalability is revolutionary.

As we move forward in the ever-evolving landscape of web development, the lessons learned from Firebase's architecture remain valuable: simplify where possible, empower frontend developers, and always question traditional assumptions about how things 'should' be done. Whether you ultimately choose Firebase or not, understanding its approach to web development will make you a better architect and developer.

Remember, the best architecture isn't always the most complex one—it's the one that lets your team build and iterate quickly while maintaining security and reliability. Sometimes, that means embracing a new paradigm, even if it challenges our preconceptions about how web applications should be built.