Integrating Firebase Authentication — Ionic Part 4

As promised in my first post, we will be connecting this app to Firebase. But I will also be including user authorization (i.e. register, login, forgot password). That’s what we will be tackling today!

Firebase has everything, from authorization to a database to file storage, one of the many reasons it’s a good choice to add to mobile apps. In this post, we will explore Firebase, create a project and make a handler component for Firebase in the app.

Step 1 — Setting up the Firebase console

But first step’s first, you need to create a project on the Firebase console. All you need is a Google account to access Firebase. So head on over here to get started. Add a new project and give it a name (I just called mine ‘Tasks’), agree to everything they ask and hit Create Project.

Now to set up the project to fit our needs.

All the areas of Firebase that we will be accessing will be found under Develop.

Namely;

  1. Authentication
  2. And Database.

Let’s have a look at Authentication.

As you can see, all methods of authentication have been disabled. For now enable the very basic of types, Email/Password, so we can begin using it to register an account.

Under templates, the many email templates for verification of email address to forget password are can be found. If you wish, you can change a few of the details, like the project name to be displayed and the name of the sender.

Now, onward to the Database section. Firebase has two types of databases;

  1. Realtime Database — a NoSQL database, that looks like one big JSON Object.
  2. Cloud Firestore — A collection of documents, which are essentially JSON Objects.

Firestore is the better option as it has a better structure compared to the normal Realtime Database. In the Realtime Database, anybody can write data anywhere, if they have the reference to the database, greatly affecting all the data stored. And for that reason, I picked Firestore and created the database in test mode, so we can assess the database.

Firestore in test mode does allow anyone to read and write into it, so let’s make it that only users who have registered to the app have access to the database. To do so, switch allow read, write: if false; for allow read, write:if request.auth.uid!=null;. Only registered users have a unique uid, with which to distinguish them. Most often, the uid is used as the ID to the users' object. I will be implementing the same for this project.

Once the rules are changed, we need to create a collection, so all our user documents can be put into it. Since we cannot have a collection without at least one document, make a fake user. You can delete it from the dashboard later.

As we have set up the Firebase dashboard, let’s move on the integrating Firebase into the app.

Step 2 — Linking it all up

There is a special module AngularFire you can download using npm to incorporate Firebase into the Ionic app. To download, type npm install firebase angularfire2 --save.

To use this module, you need to import it into the app.module.ts page, like so

import { AngularFireModule } from 'angularfire2';
import { AngularFireAuthModule } from 'angularfire2/auth'
import { AngularFirestoreModule } from 'angularfire2/firestore';

We also need to add the necessary config data for the app to access and use the correct database. This can be found in the Project Overview section, ‘Add Firebase to your web app’. You are required to call the JSON object firebaseConfig and initialize it after the imports.

export const firebaseConfig = {
apiKey: "#######################################",
authDomain: "###########.firebaseapp.com",
databaseURL: "https://###########.firebaseio.com",
projectId: "###########",
storageBucket: "###########.appspot.com",
messagingSenderId: "############"
};

One last step! You need to include the imported modules above, into the import array of @NgModule that contains all the components used in the app, initializing the AngularFireModule as well with the config object above.

@NgModule({...imports: [...AngularFireModule.initializeApp(firebaseConfig), AngularFireAuthModule, AngularFirestoreModule]})

AngularFireAuthModule comes with many methods pertaining to authorization, like signup, sign in, forgot password, etc. All the methods we will be using will be found in the auth property of AngularFireAuth. The methods being used are;

  1. signInWithEmailAndPassword() — Login
  2. createUserWithEmailAndPassword() — Register
  3. sendPasswordResetEmail() — Reset Password
  4. signOut() — Logout

Step 3 — Writing all that code

We need to add a listener, to check if the user has logged in or not, and to display the correct response for either. We need to add the listener in the app.component.ts, as it’s the first page of the app that is loaded.

const authObserver = afAuth.authState.subscribe(user => {
if (user) {
this.rootPage = HomePage;
authObserver.unsubscribe();
} else {
this.rootPage = LoginPage;
authObserver.unsubscribe();
}
});

Import the necessary other modules, like the HomePage, LoginPage, and AngularFireAuth.

Let’s start coding the Register page first.

First, to add a new page to the app. There are two ways to do this;

  1. Create a new folder within the pages folder inside src and create separate .scss, .ts and .html files.
  2. Or, be lazy (like me 😋) and just type ionic g page <name of page> in the console. All three files will be auto-generated!

Since we need to conduct many validations on the data entered in the login, register and forgot password pages, we need to utilize a form group to have a track of all the fields in the form and to add any and all validation to each field, such as checking if the email looks like an actual email, password lengths, the works. We’ll first design the view of the page. In register.html, the form tag looks like so;

<form [formGroup]="signupForm" (submit)="signupUser()" novalidate>

novalidate is used as the actual validation is being added in the .ts file to the form group signupForm.

Then copy the exact item tag that we have been using to add task names in the home page (but remove that button, id and [(ngModule)] this time!). Add a tag for the users’ full name, email, password and confirm password. The type of input tag for the latter two is password and email for the email tag. You will also need to add a formControlName to each input tag. Add in a button as well of the type submit, to submit the form. The body of your register page must now look like this;

The Register button is disabled until the Lets now add validators to each input, in the register.ts page. We will need to import the following modules to the top of the page,

import { FormBuilder, FormGroup, Validators } from '@angular/forms';

initialize the form group outside of the constructor, so it can be accessed from anywhere in the component; public signupForm: FormGroup and initialize the form builder inside the parameters passed to the constructor, like so;

constructor(public navCtrl: NavController, public navParams: NavParams, public formBuilder: FormBuilder){}

Validators will be added to the form within the constructor like so;

Validators.compose creates a validation check for the value, according to the validations passed in its parameters. Most of these Validators are self-explanatory. The pattern Validator checks if the value fits a specific regex. But one question remains, how to validate if an email looks like an email? Apparently, we need to make one….

But don’t worry! It’s quite simple and the only logic to it is to see if it fits a certain regex.

We need to make a new folder ‘validators’ in the src folder and a file ‘email.ts’ within it. We will be declaring a static method to check the email. When validating the email, we send the formControl to the Validator, so in that case, we will need to import FormControl. Once the email is tested against the regex, we need to return a value to convey if the email is valid or not. The final code for the email validator is;

Now import the EmailValidator into the register.ts and add it to the array within the Validators.compose method for the email input.

this.signupForm = formBuilder.group({
email: ['', Validators.compose([Validators.required, EmailValidator.isValid])],...});

That’s it on the validation side.

Another added feature you can do is show an error message right below the input, or even have the input tag turn red if the validation returns a false. The code for the error message;

<ion-item class="error-message" *ngIf="!signupForm.controls.email.valid  && signupForm.controls.email.dirty">
<p>Please enter a valid email.</p>
</ion-item>

*ngIf allows you to display the error only if the validation is false. The errors should be put right below each tag, altering the message and input name (in the above example ‘email’) accordingly.

The code for a red input on validation error;

[class.invalid]="!signupForm.controls.email.valid && signupForm.controls.email.dirty"

Add this inside each input, again changing the inputs’ name accordingly.

Now to handle the button click!

Create the method signupUser(). We will be using the AngularFireAuth modules’ method createUserWithEmailAndPassword(). This returns a promise, that we need to capture and according to the result, handle either the sign in of the user or display an error message. To make it more user-friendly, also show a loading carousel to the user as signup takes place.

As the button is only enabled when the whole form is valid, we do not need to recheck on that fact. We will first check if the password and the retyped password are the same, and if they are, create the new user and add their information to the Firestore. If the two are different, display an error message in the alert, stating that they are different.

You will need to additionally import AlertController, Loading, LoadingController, AngularFirestore and HomePage.

loading needs to be declared outside the constructor, so that it can be accessed by all the methods. AlertController, LoadingController and AngularFirestore needs to be initialized in the constructor parameters.

And (finally) the register page is done!

Whew! 😩😩 That’s the longest post I’ve ever written. And there’s still more to go…..

But don’t worry! The rest is all just copy + paste.

The next page to tackle is the Login page. Copy the entire Register page form to login.html, coz its time to make some changes for it to fit Login. Remove the first name, last name and retyped passwords’ input tags and error messages. On the form tag, change all instances of signupForm to loginForm.

Change the submit buttons’ text to ‘Login’ and the onSubmit method to loginUser(). Add two buttons as well, outside the form, to navigate to the register and reset password pages. The final body of login.html;

There you have it! The UI is done.

The loginForm has the same Validators for the email and password fields. So, proceed to copy the same formBuilder, omitting the first name, last name and retyped password fields.

this.loginForm = formBuilder.group({
email: ['', Validators.compose([Validators.required, EmailValidator.isValid])],
password: ['', Validators.compose([Validators.minLength(6), Validators.required])]
});

The loginUser() method has similar code to that of the signupUser method. So copy that on to the login.ts as well. The change to be made, is to remove the password comparison and accessing the database.

You will need to import the exact extra modules to the login.ts as well, with the exception of the AngularFirestore, as you will not be accessing the database now.

Now to handle the buttons to the reset password and the registration page;

resetPwd() {
this.navCtrl.push(ResetPasswordPage);
}createAccount() {
this.navCtrl.push(RegisterPage);
}

The pages work like a stack; you push the next page to the top of the stack and pop from the top as well.

Bear with me, we have one more page to go. Yay! More copy+paste!

For the reset password, we only require the email field, but still, need a form to validate the email entered. Much like for the Login page, copy the entire login.html form, remove all fields except the email input tag and error message, change all instances of loginForm to resetPwdForm. You are left with;

The same is to be done for the reset-password.ts file. The form builder looks like this;

this.resetPwdForm = formBuilder.group({
email: ['', Validators.compose([Validators.required, EmailValidator.isValid])]
});

while the resteUserPwd() method looks like so;

The handler code above pops the reset password page to show the login page once the request for the link is sent.

One last part (I’m so sorry! I’m tired too)…😅😅

The logout button, the easiest and smallest code!

You need to put a button at the end of the header on the home page as shown below;

<ion-header>
<ion-navbar>
<ion-title>To-do List</ion-title>
<ion-buttons end>
<button ion-button (click)="logout()">Logout</button>
</ion-buttons>
</ion-navbar>
</ion-header>

The code to handle the logout in home.ts;

logout() {
return this.afAuth.auth.signOut().then(authData => {
this.app.getRootNav().setRoot(LoginPage);
});
}

The code after the ‘then’ takes the user back to the login page.

And that’s it! Finally! 😩😩

To allow the app to use these pages, you need to include them in the app.module.ts page, in both the declarations and entryComponents arrays, like so;

@NgModule({...declarations: [...LoginPage, RegisterPage, ResetPasswordPage]...entryComponents: [...LoginPage, RegisterPage, ResetPasswordPage]})

Let’s have a look at all we have achieved so far.

App with Authentication

And there you have it! 😍😍 It’s not so easy on the eyes, but it is definitely functional.

As you can see, when a particular fields’ validation returns false, the input turns red, and the error message shows as well. The buttons stay disabled until all fields of the form are valid!

Below, the user object has also been stored in Firestore, with the current users’ uid as the key to the document. It all works!

Firestore Document

Next post we will integrate Cloud Firestore to save our data in Firebase.

See you next week! Ciao!

Find the commit for this step, here.

P.S. Link to the next post!

Front End Developer at Switchd Ltd. | MSc in Computer Science @ QMUL (2022)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store