Web App Authentication with Email Magical Links, SMS Security Codes, and Google Accounts Using FirebaseUI and Svelte
In a past post, I wrote about creating an essential web authentication workflow with Firebase and Vue. A reader suggested exploring an additional resource provided by Firebase called FirebaseUI. This post follows that suggestion.
FirebaseUI is an open-source UI library that sits on top of the Firebase SDK. It implements visual controls, navigation, and integration logic for web app authentication.
The main advantage of using it is to reduce coding effort and the number of bugs that always accompany new code.
Today we will use FirebaseUI to enable users to sign with the traditional email and password, but also with email magical links, SMS security codes, and Google accounts.
Prerequisites
Besides the Firebase SDK and the FirebaseUI libraries, we will build the web app using the Svelte framework. Reactive frameworks like Svelte simplify the instructions to sync UI with the application state.
You should be fine if you are familiar with the web stack (HTML, JS, and CSS) or some other web framework like Vue, Angular, and React.
In the last section, I mentioned a previous post that took the time to create a minimal auth experience using Firebase and Vue but without relying on FirebaseUI. You don’t have to read that post to understand this one. Even so, starting there could be more helpful if you are unfamiliar with Firebase Authentication.
A machine with Node.js and a code editor like Visual Studio Code are the only requirements to code along.
Nevertheless, please say in the comments If you find that any topic lacks more explanation. I will try to help the best way I can.
Up and Running
Our first milestone is to have the skeleton of our web application running.
We start by creating a project folder to hold the source code. Inside the new folder, run the following command to initialize an npm package.
npm init -y
The browser does not understand Svelte files. Vite will compile and serve our code as development goes on. Please install our development dependencies.
npm i -D vite svelte @sveltejs/vite-plugin-svelte
We instruct Vite to support Svelte with a vite.config.js
file.
Our web app requires an entry point. Time to create the index.html
file with boilerplate markup to replace later.
Next, to ensure the plumbing is all set, run Vite with the npx vite
command and check if the app is online at the address provided on the terminal.
Root
Svelte (like other component-based frameworks) abstracts the UI in a component tree, starting with a root component representing the whole app.
We create the app.svelte
file to source a root component. For now, we leave it with a placeholder message. Then we update the index.html
file to bring the root component from app.svelte
and render it a target div
.
Sweet 😎. Now we move to Firebase.
Firebase
Now we go to the Firebase console to create new a project, activate a web app inside the project, and bring the configuration data into a new firebase.config.js
file in our source code.
With that covered, please go back to the Firebase site and click on the authentication service link. Choose the get started option and click on the email/password button. Enable email/password and email link on the next page and save. Also, enable phone and google providers with their default configuration.
Firebase is ready for our web app now. Let’s code the integration on the frontend side.
Integration
Our app requires a logic layer that receives data and actions performed by the user and communicates with the Firebase authentication service to establish new states like the user is now signed in or signed out.
We use two firebase packages to do the heavy work for us. The first is the firebase SDK which encapsulates the communication between our web app and Firebase services. Secondly, the firebaseui package will render the visual controls regarding the sign-in while driving the SDK.
So open a new terminal window (without closing the one where the Vite process is running) and install both libraries.
npm i firebase firebaseui
Our web app components will need three functions to take advantage of those libraries’ resources. The first function starts and passes the control to FirebaseUI, another for signing out (which is not covered by FirebaseUI), and the last to execute callbacks whenever the auth state changes.
Please create an firebase.auth.js
file to make it a central place to expose those functions mentioned before. There, we initialize the Firebase connection and, for now, leave the functions with boilerplate code that we will replace as needed in the following sections.
Flow
Let’s consider how our app should behave regarding its two primary states. On the one hand, if the user is signed in, we must show a welcome page with business data and an option to sign out. On the other hand, if they are signed out, they should see a sign page with the controls provided by FirebaseUI.
We can subscribe to a Firebase trigger that fires every time this auth state changes, no matter the mechanism. The trigger will invoke a callback we provided upon subscription, passing the user profile data (if signed in) or null
(in case of sign-out).
With that, we can develop a centralized solution at the root component to decide which of the two app pages appears.
To support that central switch, we will need to expose the capacity of the onChange
function at the firebase.auth.js
file. It must support the invocation of arbitrary callbacks whenever Firebase detects a state change.
Last, we build a dummy version of the PageSign
and PageWelcome
components. With that covered, we can see a glimpse of our app.
At the root component, Svelte takes care to rerender the template when the status
value updates. This reactive pattern put together with the if
and else
blocks guarantees that the app shows correct content based on auth state.
Time to put FirebaseUI to work.
FirebaseUI
Since the root component will render PageSign
when the user is signed out, that will be the place to ask FirebaseUI to control the app flow.
First, we should update the startUI
function exported from the firebase.auth.js
file to do just that. It envelops the FirebaseUI start
function and exposes the feature in a pre-configured form.
The start
function from the FirebaseUI library takes two arguments. The first is the element id which will contain the library’s pre-made user interface, and the second is a configuration object with a flexible list of options.
As FirebaseUI configuration options go, we need to inform the target providers: email, phone, and google. We also set the signInSuccessWithAuthResult
callback to do nothing and return false.
Our web app needs this awkward callback behavior so FirebaseUI does not redirect (forcing a refresh) after completing the sign-in process.
Next, we return to the page-sign.svelte
file and create an empty div
containing the FirebaseUI magic. We use the Svelte onMount
hook to execute the startUI
function. The hook runs instructions once at the beginning of a component lifecycle.
The number of features we gain when putting these libraries together is impressive. I can only imagine the wonders you folks will build on top of that.
Hi, a small break to say that you can subscribe for free and never miss new posts from the blog.
Welcome
Now that users can sign in, we could improve our welcome page to show some data about the user and enable sign-out.
First, since the root component already passes down the user email and id values, we need to declare them as properties in the PageWelcome
component so we can refer to them in the template.
Next, we update the signOut
function in the firebase.auth.js
file. Then import it inside the PageWelcome
component. With that settled, tie the function to the click event of a button in the PageWelcome template.
Our tutorial is good as done. You can refer to the complete source code here.
UI Libraries
I’m always mesmerized by how fast we deliver professional-looking experiences using component libraries like FirebaseUI. It also makes me remember my first days developing for the web and how Bootstrap helped me alleviate the backlog of new things to learn.
You see, the web is made of so many moving parts. It’s easy to get overwhelmed by a gigantic learning roadmap, and new things are constantly popping up. In my opinion, this role as a stepstone in the learning path to senior skills is the main advantage of using such component libraries.
I don’t embrace their speed in development so much for projects with a long-term perspective. After some years in the industry, I came to the opinion that the trade-offs in rigidity eclipse the legitimate gains in dev speed. That does not include short-term apps like those made for hackathons or prototypes.
In my experience, always comes the moment when we must implement something the library can’t offer. Then you need to reproduce its look and feel in a new component. Perhaps its version starts to conflict with crucial elements like the underlying framework. You will now need to replace or freeze the version of one of them. Those problems are hard to solve and hinder app sustainability.
Nevertheless, take my opinions with a grain of salt. These open-source projects have helped millions in our community (including me) for free over the years. Their maintainers deserve a lot of respect.
The personal lesson I want to share is this: be aware of the trade-offs involved when deciding to use a UI library versus building your components.
I would love to learn what you think about the subject and the post in general. Please share your thoughts in the comments.
If you think this post can help someone, please share it with them. Better yet, subscribe for free and receive fresh content in your inbox whenever it is published on the blog. I will never send more than one email a week.