How to authenticate users with Vue.js and Firebase?

In this lesson, I'm going to show you how to create a Vue.js project using the CLI. When the project is created, I'm going to create a service for interacting with Firebase and make it possible to sign up, sign in and sign out.


Creating a Vue.js project

1. Let's begin by writing this command in a terminal. This will launch the Vue CLI3 which has a wizard helping us create a project.

2. We want to manually select which features to use. In the next step, I will explain which features we need.

3. "Babel" is the first feature to select. "Babel" is used to help us write backward compatible code. The code we write will be converted to JavaScript that older browsers will understand. The next feature to select is "Router". The "Router" makes it possible to navigate around the app. Let's say we want to go to "/about". The "Router" will tell our Vue.js application which components to use and so on. The last feature we need for this project is "Vuex". "Vuex" is a helper for state management. We need state management to make it easier to check throughout the app if the user is authenticated or not. You can think of "Vuex" as a place to save variables and data that can be used everywhere in the application.

4. I don't need the fancy HTML5 history mode for this application. You can add it if you want because it won't make any difference before we deploy the code to a server.

5. I have selected "In dedicated config files". I select this because I feel it gives me more control and the code will be a little bit more separated and maintainable.

6. I don't save this preset because I will probably want to select a different feature or something the next time I create a project, so it's not necessary for me.

7. The project is created. Let's go into the folder and run "npm run serve". The server will launch and you can open "http://localhost:8080" in your browser to see the default Vue.js welcome page.

Installing Firebase and Bulma

Since this lesson is about Firebase authentication we obviously need the Firebase module. Since we're here to learn Firebase and Vue.js, I have decided to use the CSS framework Bulma. This makes it possible for us to just set up the structure of the app using HTML and not spend a lot of time with CSS.

So if you run the command above and wait for the installation to finished, you can run the "npm run serve" command again. The website should look something very similar to this now.

It's a very basic page with some information about Vue.js and a menu showing you that the router is working. If you click on the "About" link, you'll be sent to the "About" page.

Creating a Firebase project

It's time to log in to your Firebase account (Create one if you don't have) and create the project there.

When the project is created you can click the embed button and copy these lines of code, we need them in the next part of this lesson.

There is a menu on the right side of the screen, click on authentication. And then on "Set up sign-in method".

In this lesson, we're only going to focus on Email/password authentication. So if you select this option in the list and then check the "Enable" button and "Save". Everything is now set up correctly in Firebase and we can head back to the code.

Creating a Firebase service

Create a new folder in the "src" folder and name it "services". Inside this folder, you can create a file called "database.js".

We need to import Firebase. So at the top of the file put this line.

Next step is to paste the lines of config we copied from Firebase. This is API-keys and some other properties to tell our application how to connect to Firebase.

At line 12 we create a new variable "database" and assign this to "firebase.initializeApp(config)". Firebase is now initialized and ready to use. But in order to use this "database" variable other places in the app, we need to export it.

We can continue to Vuex now. Open the file called "store.js".

To make it easier to check if the user is authenticated we use something called a Vuex store. A Vuex store handles the state of the app. You can think of this as global variables.
#1 and 2, import Vue and Vuex
#4, tell Vue to use Vuex
#6, create and export the store so it can be used everywhere in the app
#7, in this object (state) we set all of the global variables
#8, currentUser is the variable that will contain information about the user
#10, mutations is a list of functions for mutating (changing) the current state
#11, setCurrentUser takes to parameters. "state" = the current state and "payload" will in this case either be the user object or "null"
#12, here we assign the state.currentUser with the user object we pass in

We can go back to "database.js" and import the store we just created.

When the store is ready, we can continue to create the actual sign-up function that will be used in our app. You can insert this code on the line under "const database = firebase.initializeApp(config)".

#15, create a function called "signUp" and attach it to the "database" object. This will be an async function, meaning that it will not return anything before we have received an answer from Firebase. We'll pass in two parameters: email and password:
#16, the reason we use a try/catch method here is that if the sign up fails in a way, we'll get an error from Firebase we can show the user.
#17, we put "await" at the beginning, because we don't want to continue this function before we get an answer from firebase. The function we call (createUserWithEmailAndPassword) is pretty self-explanatory. We create a new user by passing in the email and the password.
#19, if we reach this line, we know that the user has been created. Then we can go on and commit to the store, and call the function "setCurrentUser" and we'll pass in the user object. As you can see, we can get the current user object when we are authenticated.
#21, return true so that we know everything is okay.
#23, if there is an error, we want to return this so it can be used.

Creating the sign up view

Create a new file inside the "views" folder and name it "Signup.vue". You can make it look like this.

To be able to navigate to this page, we need to import it to the router.

#4, import the view/component we just created
#15-19, create the route object. Set the path, the name and which component/view to use.

We can also add a link to the sign up page. If you open up "App.vue" and just replace the current about link that is already there with this one.

If you open the page in a browser now and click "Sign up" in the menu, you should see something like this.

Let's continue to add the sign up form. Open up "Signup.vue" again.

#5, "@submit" is an event listener. So when the form is "submitted", we want to call the "signUp" function. We add ".prevent" there to stop the default form action which will refresh the browser.
#7, we add an email field here. This field needs to be bound to the data object, so we use the "v-model" directive. Inside this "v-model" attribute, we pass in the variable we want to bind this too.

The form is finish, but we need to create the method that will be called when the form is submitted.

#20, import the service we have been creating
#24-30, add a data object with email and password for the form. The "error" variable is needed to present an error message if there is one.
#32, the method that is called when we submit the form
#33, create a new variable "result" and assign this to the value we get back when we call the service function we created (database.signUp).
#35, if the "result" variable contains a ".message", we know that there is an error. Only when there is an error, the ".message" will be available.
#36, assign the "error" variable in our data object to the "result.message".
#38, if there isn't any error message, the user is created and we can print this message in the console.

Right under the form, you can fill in this code. We use a Vue directive called "v-if". So this checks if there is an error. If there is an error, this element will be rendered and a red message box will be showing.

The last step before we try this is to import Bulma, so the styling will be applied to the elements we've been adding.

If you open the page in the browser again and navigate to "/signup", you can test the form. If you submit it with no value or just a fake email address and no password, the error message will be showing and telling what the error is.

The form is filling up the whole screen, and that looks a little bit weird. Let's add a columns wrapper and a column around the code inside "Signup.vue". "is-4" will make it narrow and "is-offset-4" will center it on the screen.

So now it looks much better.

If you try to create an actual user now and open up the console, you will see that "User is created" will be printed at the bottom.

Creating the sign in view

We can begin by creating a new file inside the "views" folder. Call it "Signin.vue". Copy all of the code from "Signup.vue" and paste it in the "Signin.vue" file.

Next step will be to replace all occurrences of "Sign up" with "Sign in".

We also need to add this to the router. So import the new file we created and add it to the router object.

We can also add the link inside "App.vue" to make it possible to just click our way into the view. When you've done this, you can open the page in the browser, and it will look something like this.

Create the sign in function in our service

If you open up the "database.js" service we created, you can make a copy of the signUp method. Rename this to "signIn" and replace "createUserWithEmailAndPassword" with "signInWithEmailAndPassword". The rest of the function can be as it is.

We can now test this function by opening the site in a browser and sign in with the username and password you created earlier.

Let's make it possible to sign out

Open "database.js" again and make a copy of "signIn". Rename this with "signOut" and remove the email and password parameters. You can also replace "signInWithEmailAndPassword" with "signOut" and remove the parameters.

Now that we're not authenticated anymore we need to set the user to be null instead.

Next step to fix now it a sign out button, and hide the sign in / sign up buttons in the menu. Open up "App.vue" and make these changes.

#5-8, add a template wrapper around the two "sign up" and "sign in" buttons. We use the "v-if" directive to make sure this only show when we're not authenticated.
#9-11, add a new template element and a sign out button inside. We use the "v-else" directive to make sure this only show when we're authenticated. When we click the button, we call the "signOut" method we'll very soon create.
#17-25, add a new script tag and put this code inside. We'll use a computed property to get the user object from the store. When this is added, we can use "currentUser" as a variable inside this template. We return the value of "this.$store.state.currentUser" which will either be a user object or just "null".

#18, import the database service
#27, the sign out method which will be called when we click the sign out button. This method will call the "signOut" method inside our database service.

If you sign in now, you will see that the "sign up" and "sign in" buttons will be hidden and a "sign out" button will show instead.

Check with Firebase if we're authenticated

If you sign in to the application and refresh, you will see that you are signed out automatically. This is because the app is created before we have received any information from Firebase. Open up "main.js" and make these changes to the code.

#5, import firebase
#9, create a variable for holding our app
#11-19, wrap the app creation in a function. This will be called after we receive information from Firebase.
#21, when we launch the app in our browser, we'll get a callback from Firebase. We receive a user object, which either can be an actual user or just null.
#22, check if the user isn't "null".
#23, set the state/vuex current user to be "user".
#25, if we reach this line, it means we are not authenticated and we want to set the user to "null".
#28, call the initialize function we created at line 11.

If you go back to the browser now and refresh, you will see that we are still logged in!
Firebase handles the sessions for us, so when the app launches we ask Firebase if we are authenticated before the app is actually created.

The profile view

Let's create another view called "Profile". This will only be available if you're an authenticated user.

#5, print the user's email address.
#13-15, the same computed property as we created for the navigation.

Import the new view into "router.js" and add it to the router object.

If you open up the browser now and navigate to "/profile", you will see the profile page and the email address to the user you're signed in to.

Open "App.vue" and make this little change. So that when you sign out, you will be redirected to the sign in view, instead of the profile view which no longer will work anyway.

We can do the same change to the "Signin.vue". So under the line where we log to the console, add this line where we redirect the user to the "/profile" page. If you want, you can add this to the "Signup.vue" file too.

Router guards

If you sign out of the app now you'll be redirected to "/signin", but what happens if you navigate to "/profile" without being authenticated?
You'll get an empty page!

We need to add something called router guards. This will make it possible to check if the user is authenticated before each of the routes we visit.

First, replace first lines in "router.js" with this one. This assign the whole router to a variable we created "router".

We need to do several changes to the bottom of this script.

#31-33, add meta data to the profile router. This way we can tell the router that this view requires authentication.
#44, })
#46-54, we use a function called "beforeEach". So before each route we visit, perform this code.
#47, check if the meta of the route has "auth" = "true". We also check if the state has a "currentUser" variable.Importantreplace "next" with "to".
#48-50, if the auth is set to true and there isn't a "currentUser", change the "next" route to be "/signin".
#52, go to the next route
#56, (you can't see this line) = export default router

#7, import the store/vuex at the top of "router.js".

If you now open up "/profile" in the browser, you should be automatically redirected to the "/signin" view.

Summary

This lesson is now finished and you should have an app that has all the basic authentication you need. Sign up, sign in and sign out. In addition to this, we have created a profile page, and this is guarded by checking if the user is authenticated before we enter this route.

If you got any questions about something in the code or anything, please feel free to send me an email at hello@vuehero.com and I'll answer you as soon as possible!