modernizing-and-scaling-our-authentication-with-firebase
Engineering
Nov 9, 2020

Modernizing and Scaling our Authentication with Firebase

Bruno Moraes
Senior Backend Engineer

Bruno Moraes, senior backend engineer, and João Guilherme, senior full stack engineer, both work on the Traveler’s Platform mission team. The team's goal is to build cross-platform traveler experiences. Bruno and João take us through their steps, challenges, and learnings while migrating our legacy website and apps authentication system to Google's Firebase.

First, why did we migrate to Google's Firebase? As the company's systems and architectures evolved, we faced a problem. Our authentication systems, which lived inside a monolithic application, no longer fulfilled our needs. We needed a simple, future-proof authentication mechanism to allow our teams to build features and services in a new architecture.

The new solution would need to fulfill the following requirements:

  • Provide SDKs for our backend clients: Website, iOS, and Android apps
  • Follows industry-standard authentication methods
  • Token-based: enables our clients to access secured API endpoints directly after authentication
  • Supports social identity providers (IdP) like Facebook, Google, and Apple login
  • Multi-region setup for redundancy and resilience
  • GDPR-compliant

The initial question was, should we build it internally or use an external provider? After internal discussions and analysis of everything that needed to be built, the answer was clear. We would search for a third-party provider. An off-the-shelf solution would allow us to deliver this project in a shorter amount of time, enabling us to focus on building features for our customers.

We analyzed different solution providers, including Auth0, Okta, AWS Incognito, and Firebase. After comparing their offerings, it became clear that they all covered our requirements mentioned above. We chose Firebase due to their pricing and provisioning model and their straightforward integration documentation.

You might also be interested in How we split a monolith into microservices.

Customer database migration

After settling on a vendor, we got down to work. One of the first things we had to define was how we would integrate Firebase into our frontend clients and migrate millions of existing customers. We needed to make sure that they could log in or sign up seamlessly whenever they returned to GetYourGuide's website or apps.

When designing a migration plan, it's essential to consider the sheer complexity of migrating from legacy systems to new ones. It's crucial to have a plan that covers all cases (especially the edge ones).

To get the migration started, we created a script that would register our customers into Firebase on a batch basis. The script read our existing customer database, compared it with a control table that contained already migrated customers, and then communicated with Firebase via their Admin SDK to create customers there. Migrating all the customers took us around four days. We did it at a consistent pace so that we wouldn't abuse Firebase's API. We also ran the script as a cron job every minute, so when new customers signed up, they were also created in Firebase.

Once our customers were migrated to Firebase, it was time to start working on integrating with Firebase's frontend clients SDKs (website and apps).

Integrating Firebase with our web applications

There was no option aside from using Firebase Node and the Javascript (client-side) SDKs in the web part. We began by doing incremental steps: first, we wanted to make sure login information would be available on the server and the client-side right away for any feature/page across the GetYourGuide website. You might be wondering, "Why in both places?" Here's why:

First, it's important to remember that Firebase covers the support services developers would typically have to build themselves, so they can focus on building the actual app experience. The Google product's toolset includes things like analytics, authentication, databases, file storage, push messaging, and the list goes on. We mainly use it from an Authentication perspective since it would help us log customers in while fulfilling the requirements mentioned above. If you're new to Firebase, this Medium article offers an excellent summary.  

You might also be interested in: How we migrated a microservice out of our monolith

Legacy support

Due to the massive migration from our existing monolithic architecture, we still supported authentication in the legacy application in a few pages. This meant we would log customers into Firebase (new application) and the legacy system to display the user information on all pages consistently.

For that, we had to maintain two new features: an endpoint for logging customers into the legacy pages and a new frontend component, which would essentially be the login and the sign up modal.

The endpoint was built on top of our legacy codebase, where it received a JWT token from Firebase and login customers on the old platform — this part was quick and took us only about an hour of effort.

However, to deal with the new component — which was built on the new platform — we had to add an endpoint on the new platform that could render these modals (login, sign up, and logout feature) and return the HTML response + its resources (Javascript and CSS) to integrate it with the legacy system. With that, we would have only a single entry point for these components, and they would get rendered both in the new and in the old platform.

The `Log in` and `Sign up` action buttons on the top right of our legacy pages are components coming from our new frontend system.
The `Log in` and `Sign up` action buttons on the top right of our legacy pages are components coming from our new frontend system

Restricted endpoints on server and client-side

Regarding using the Firebase framework on the frontend, as mentioned above, we used the Firebase Admin SDK for node and the client-side version of it for browsers. Given that, we could use authenticated endpoints on the server-side and the client-side whenever you called our http client where. Essentially, we attached a firebase JWT token depending on the environment you are — server vs. client to the requested endpoint.

The overall implementation of Firebase was smooth. Incremental steps were (and still are) part of our end-to-end development regarding big features like this.

{{quote}}

The team had an exciting initiative to test our login/sign up features. We held testing parties where we gathered stakeholders and a few engineers. They tried to log in or sign up, and we ended up doing lots of bug fixes around the mechanisms. The testing phase helped us identify odd behaviors along with the development timeframe.

We are currently using Vue in our new frontend application, both on the server-side and the client. We had to support user information getting stored in our Vuex (which is essentially a state management library responsible for storing states across components/pages.

You might be wondering, “What happens under the hood when logging customers into the platform?” It turns out we call a few endpoints: the legacy firebase support, the actual firebase login, the “backend for frontend” login (login on the new frontend part on the server), and once this is complete (if nothing fails), the customer gets redirected to the same page or the page they were trying to access before but could not since it was unauthorized. Everything happens under an action which is triggered by customer’s interactions on login/sign up modals.

These are the modals - built in Vue - responsible for logging in and signing up customers across the new and legacy frontend platforms.
These are the modals - built in Vue - responsible for logging in and signing up customers across the new and legacy frontend platforms

When failures or any issues on the login method occur within these customer actions, we treat them as UI errors by displaying warning messages underneath the specific fields. This way, customers can be aware of what happened since their last interaction with the feature.

Therefore, if everything goes smoothly, all the responses should redirect them back to wherever they were before logging in to the website. We're handling the local redirect logic based on the page that the customer requested to login/signup beforehand (logging in from cart page, homepage, and so on). This allows other teams to use common actions to login customers and do different things on the UI as they respond when requests go successful.

You might also be interested in: How we find and fix OOM and memory leaks in Java Services

The workflow

TFE = the new frontend platformFishfarm = our lovely legacy system
TFE = the new frontend platform. Fishfarm = our lovely legacy system

Integrating Firebase with our native applications

The approach was slightly different for our native applications because we had to ensure that previous versions remained fully working. So we couldn't simply turn the switch to Firebase and move on. We had to keep backward compatibility for customers using older versions of the apps, and the process should also work seamlessly for them.

We officially released the integration with Firebase in iOS and Android in version 3.48, and for that, we used Firebase's official SDK for iOS and Android. For the customers using this new version or a later one, when the sign up or sign in process was triggered, the apps would communicate with Firebase to exchange credentials for a JWT token. This token would be sent in the headers of the requests to our backend services, so we could validate it.

Gather engineers by their domain expertise to test different behaviors from the login/signup perspective to validate an end-to-end process point of view.

Previous app versions would have to keep working as expected, so we didn't drop support for our old authentication method, which was also token-based. Our backend knows when to expect which token given the app version. The workflow below describes in more detail the necessary steps for our iOS and Android apps to execute a sign in process (for sign up, the process is the same).

migration-to-firebase.png
Workflow

Monitoring


To monitor customers signing up and logging in successfully during and after the migration period, we pushed relevant metrics into our monitoring backend. We created dashboards that showed us whether everything was functioning as expected. Additionally, alerts were created based on these newly created metrics, so we'd get notified if something went wrong.

You might also be interested in: How we migrated a microservice out of our monolith

Conclusion

When deciding between building an authentication system yourself or using a third-party one, it's essential to understand all the requirements and use cases. Write a list of your needs, challenges, and the benefits of both options. Propose them to the main stakeholders who will be involved and gather feedback. You'll start to see the whole picture and assess if developing an authentication system internally or buying it from another company makes sense.

Once you've decided on using a third-party system, shortlist at least three. Compare, and see which one covers your cases. When designing a migration plan, it's essential to consider the sheer complexity of migrating from legacy systems to new ones. It's crucial to have a plan that covers all cases (especially the edge ones). You must spend some time carefully looking at your code and searching for all authentication-related parts so you can build a solid migration plan. But don't over plan. There will always be unforeseen exceptions you'll need to handle as they come. Here are some final tips:

  • Give extra attention to the data transfer and context switch between your legacy and new systems. You don't want your client to be logged in on one page and logged out in another.
  • Release iteratively instead of doing a one-time big release. This way, you learn fast and can identify any possible issues quickly.
  • Gather engineers by their domain expertise to test different behaviors from the login/signup perspective to validate an end-to-end process point of view.
  • Don't forget to celebrate after completing this complex project!

If you're interested in joining our team, check out our open roles.

Other articles from this series
No items found.

Featured roles

Marketing Executive
Berlin
Full-time / Permanent
Marketing Executive
Berlin
Full-time / Permanent
Marketing Executive
Berlin
Full-time / Permanent

Join the journey.

Our 800+ strong team is changing the way millions experience the world, and you can help.

Keep up to date with the latest news

Oops! Something went wrong while submitting the form.