Back to Diary
Technical

One Account, Two Doors - Building Authentication Across Platforms

Bjarne
5 min read

Imagine you are building a house with two front doors. One door is for guests arriving by car (the mobile app), and another for those walking in from the street (the website). Both doors need to lead to the same living room, and you need to recognize who is who.

This is the challenge I faced with Booklynx. Users can create accounts through the Android app or through this website. Both need to work seamlessly, creating the same user experience regardless of where someone signs up.

The Problem: Two Entry Points, One User

When a user creates an account, several things need to happen:

  • A Firebase Authentication account is created (email/password or Google Sign-In)
  • A user document is created in the database with profile information
  • A unique friend code is generated (like ABC23XYZ)
  • Default libraries are set up (Reading, Want to Read, Finished, etc.)
  • Privacy settings and preferences are initialized

If I put this logic in the mobile app, what happens when someone signs up on the website? I would have to duplicate all that code. And if I ever change how user setup works, I would have to update it in two places. That is a recipe for bugs.

1
Mobile AppUser creation code
2
WebsiteSame code, duplicated
3
DatabaseInconsistent results?
The Problem: Duplicated Logic

The Solution: Cloud Functions

Instead of putting the user creation logic in each client (app and website), I moved it to a Cloud Function. Think of it as a trusted butler who lives in the cloud. Both doors can ring the butler, and the butler handles everything consistently.

Mobile App

  • Signs up user
  • Calls Cloud Function

Website

  • Signs up user
  • Calls Cloud Function

Cloud Function

  • Creates user document
  • Generates friend code
  • Sets up libraries
The Solution: Single Source of Truth

Now, whether you sign up on your phone or on this website, the same Cloud Function runs. It creates your profile, generates your unique friend code, and sets up your default libraries. One piece of code, used everywhere.

Simplified Cloud Function
// Both app and website call this function after signup
export const createUserSetup = onCall(async (request) => {
  const uid = request.auth.uid;
  
  // Generate unique 8-character friend code
  const userCode = await generateUniqueCode();
  
  // Create user document with defaults
  await db.collection('users').doc(uid).set({
    email: request.auth.token.email,
    displayName: request.data.displayName || 'Reader',
    userCode: userCode,
    createdAt: serverTimestamp(),
    // ... privacy settings, notification preferences, etc.
  });
  
  // Create default libraries
  await createDefaultLibraries(uid);
  
  return { success: true, userCode };
});

Why Email Verification Matters

There is another challenge with authentication: spam and bots. Without verification, anyone (or any script) could create thousands of fake accounts, pollute the voting system, or flood the community with spam.

That is why Booklynx requires email verification before you can vote on features or participate in discussions. When you sign up, we send a verification link to your email. Until you click it, you can browse the site but cannot interact with the community.

Why this protects everyone

Email verification ensures that every vote and every comment comes from a real person. It keeps the feature voting fair and the discussions genuine.

The Exception: Social Sign-In

Here is the clever part: if you sign in with Google or Apple, you skip the email verification step entirely. Why? Because Google and Apple have already verified your email for you.

When you use "Sign in with Google," Google confirms that you own that email address. They have already done the hard work of fighting bots and fake accounts. I can trust their verification, so you get instant access to all features.

1
Email SignupVerify via email link
2
Google/AppleAlready verified
3
Full AccessVote, comment, participate
Email vs Social Sign-In

The Technical Dance

Getting all this to work smoothly required careful choreography:

  • Firebase Authentication handles the actual login (passwords, Google tokens, etc.)
  • Cloud Functions create the user profile and setup after authentication succeeds
  • Real-time listeners on the website detect when the Cloud Function finishes, so your profile appears instantly
  • Security rules ensure only the Cloud Function can create certain data, preventing tampering

The trickiest part was handling the brief moment between when you sign up and when the Cloud Function finishes creating your profile. The website now uses a real-time listener that automatically updates the moment your data is ready. No refresh needed.

The Result

Whether you sign up on the app or the website, with email or Google, you get the same seamless experience. Your account is ready in seconds, and everything just works.

Lessons Learned

1. Centralize Critical Logic

Anything that must work the same way everywhere belongs in a Cloud Function or backend service. Client code (apps, websites) should be thin and focused on the user interface.

2. Trust, But Verify

Email verification is a small friction that provides huge protection. But when a trusted provider like Google has already verified someone, do not make them jump through hoops again.

3. Plan for Multiple Clients

Even if you are building just an app today, design your backend as if you will have a website tomorrow. It is much easier to add a second door when the butler already knows what to do.


Ready to create your account?

Sign up and start tracking your reading journey today.

Create Account
authenticationfirebasecloud-functionssecurity
B
Bjarne
Building Booklynx with love for readers everywhere