• Skip to primary navigation
  • Skip to content

JAVEBRATT

Ionic Framework Tutorials

Looking for something?

  • Firebase Guides
  • About Me
  • Firebase FREE Course
  • Ionic & Firebase BOOK
  • Let’s Talk

Ionic Firebase Authentication: Step-by-Step Guide

June 27, 2016 by javebratt 1 Comment

Last updated on September 24th, 2018 |

If you’ve ever built your authentication system you know it can be a pain, setting up secure servers, building the entire back-end, it can take a while when all you want is to focus on making your app great.

That right there is the main reason I chose Firebase as my backend.

In this guide you’ll learn how to create an email and password authentication system, it will let your users do three basic things every app needs to do:

  • Create a new account.
  • Login to an existing account.
  • Send a password reset email.

Setup

I think that’s enough for an intro so let’s get down to business, by now, you should have created and initialized your Firebase app if you haven’t then go through this post first.

If you’ve already initialized your app, take a moment to go into your Firebase console, and inside the Authentication tab, enable the email and password authentication method so we can use it.

ionic firebase authentication

Redirecting Authenticated Users

Now that Ionic knows how to talk to our Firebase application, we’re going to create an authentication listener.

It’s a connection to the Firebase app that listens for changes in the authentication state of the user and responds to them.

For example, if you create an account and start using the app for a while, then close it, when you come back to use the app again, it should understand that you’re already a user and send you to the home page, instead of having you log in again.

For that we’re going to open app.component.ts and remove the assignment from rootPage (I’ll explain why in a minute):

rootPage: any;

After that, we’re going to create the authentication listener, for that, we’re going to be using Firebase’s onAuthStateChanged(), you can read more about it in Firebase Official Documentation.

But the TL: DR is that it adds an observer for auth state changes, meaning that whenever an authentication change happens, it will trigger the observer and the function inside it will run again.

Inside app.component.ts constructor add:

const unsubscribe = firebase.auth().onAuthStateChanged(user => {
  if (!user) {
    this.rootPage = 'LoginPage';
    unsubscribe();
  } else {
    this.rootPage = HomePage;
    unsubscribe();
  }
});

For that to work, you need to create the login page, so open your terminal and type:

$ ionic generate page Login

OK, let me explain a bit more about how the onAuthStateChanged() function works:

It is listening to the authentication state, how? when you log in or signup using Firebase, it will store an auth object inside your localStorage.

This object has information about the user’s profile, such as user’s email, name, ID, what kind of authentication used, etc.

So the onAuthStateChanged() function looks for that object to see if a user already exists or not.

If the user doesn’t exist, the user variable will be null, which will trigger the if statement and make the rootPage = 'login'.

But if there’s a user, it will return the user’s information, at that point the listener is going to send the user to the HomePage, since the user shouldn’t need to re-login inside the app.

The unsubscribe(); is because we’re telling the function to call itself once it redirects the user, this is because the onAuthStateChanged() returns the unsubscribe function for the observer.

Meaning it will stop listening to the authentication state unless you run it again (it runs every time someone opens the app).

You could technically remove the unsubscribe() and let the function keep listening, but I find it’s a good practice to unsubscribe in most of the cases, because if there’s a sudden change in the authentication state that function will trigger.

For example, if you create a function to edit the user’s email or password firebase will ask you to re-authenticate, and when you do, it will move you out of the page and send you to the HomePage if the observer is still active.

So yeah, even tho you can technically stay subscribed, think about the full flow of your app and see if that’s a good idea.

Now, remember when I said:

remove the assignment from rootPage (I’ll explain why in a minute)

Did you notice that we’re assigning the home page by its class name, but the login page by a single string?

That’s because of how code splitting works, all of our pages, except HomePage, are declared in their page modules, to call those pages, we need to use the string name they have, by default, that name is the same their class name.

This post is part of my FREE course: Build your first Firebase powered Ionic app.

Building the Authentication Provider

We’re going to create an authentication service, in the setup part of the app we created all the providers we need, for this one, we’re going to use the AuthProvider to handle all the authentication related interactions between our app and Firebase.

To create the provider, open your terminal and type:

$ ionic generate provider Auth

It will create a provider called AuthProvider, open the file providers/auth/auth.ts, delete the methods it has and leaves it like this:

import { Injectable } from '@angular/core';

@Injectable()
export class AuthData {
  constructor() {}
}

We’re going to start building on top of it, the first thing we’ll do is to import Firebase:

import firebase from 'firebase';

We need to create four functions inside this file, do you want to guess which functions we need? We need the user to be able to:

  • Login to an existing account.
  • Create a new account.
  • Send a password reset email.
  • Logout from the app.

The first function we’ll create is the login function, for that go ahead and create a loginUser() function that takes two string parameters (email, password):

loginUser(email: string, password: string){...}

And inside the function we’ll create the Firebase login:

loginUser(email: string, password: string): Promise<any> {
  return firebase.auth().signInWithEmailAndPassword(email, password);
}

We’re using the Firebase signInWithEmailAndPassword() method, the method takes an email and a password and logs the user in.

The way Firebase works, it doesn’t have a regular “username” login, your users will need to use a valid email as a username.

If the function has an error, it will return the error code and message. For example, invalid email or password.

If the function goes through, the user will log in, Firebase will store the authentication object in localStorage, and the function will return the user as a promise.

The second function we need is a signup function, but that’s not all it has to do when a new user creates an account, we want to store the user’s email in our database.

SIDE-NOTE: Database and authentication aren’t “connected”, like that, creating a user doesn’t store its information inside the database, it stores it in the authentication module of our app, so we need to copy that information inside the database manually.

signupUser(email: string, password: string): Promise<any> {
  return firebase
    .auth()
    .createUserWithEmailAndPassword(email, password)
    .then( newUser => {
      firebase
      .database()
      .ref('/userProfile')
      .child(newUser.uid)
      .set({ email: email });
    });
}

We’re using the createUserWithEmailAndPassword() to create our new user (the name kinda says it all, right?)

This function is cool, because after the user is created the app also logs the user in automatically (this wasn’t always true) meaning we don’t have to call the login function again.

The function returns a Promise (we’ll talk about them later) that will run some code for us when it’s done creating the new user and login him into the app:

firebase
  .database()
  .ref('/userProfile')
  .child(newUser.uid)
  .set({ email: email });

That’s a reference to the userProfile node inside our database.

firebase database

That reference is creating a new node inside the userProfile node, the user’s UID identifies the node, the UID is Firebase automatic id generated for the user, it’s its unique identifier.

And it’s adding a property to that node called email, filling it with the new user’s email address.

We now need a function to let our users reset their passwords when they can’t remember them.

resetPassword(email: string): Promise<void> {
  return firebase.auth().sendPasswordResetEmail(email);
}

We’re using the sendPasswordResetEmail() it returns a void Promise, meaning that even tho it does return a Promise, the promise is empty, so you use it to perform other actions once the password reset link is sent.

Firebase will take care of the reset login, and they send an email to your user with a password reset link, the user follows it and changes his password without you breaking a sweat.

And lastly we’ll need to create a logout function:

logoutUser(): Promise<void> {
  return firebase.auth().signOut();
}

That one doesn’t take any arguments it checks for the current user and logs him out.

It also returns a void promise, and you’ll mainly use it to move the user to a different page (probably to LoginPage).

And there we have all of our functions ready to use. Next, we’ll be creating the actual pages: login, signup, and password reset.

The Authentication Pages

By now we have a complete service or provider called AuthData that it’s going to handle all the Firebase <<>> Ionic authentication related communications, now we need to create the actual pages the user is going to see.

We’ll start with the login page:

THE LOGIN PAGE

Open pages/login/login.html and create a form that captures the email and password and sends them to a login function:

<ion-content padding>
  <form [formGroup]="loginForm" (submit)="loginUser()" novalidate>

    <ion-item>
      <ion-label stacked>Email</ion-label>
      <ion-input formControlName="email" type="email" placeholder="Your email address"
        [class.invalid]="!loginForm.controls.email.valid && blur"></ion-input>
    </ion-item>

    <ion-item>
      <ion-label stacked>Password</ion-label>
      <ion-input formControlName="password" type="password" placeholder="Your password"
        [class.invalid]="!loginForm.controls.password.valid && blur"></ion-input>
    </ion-item>

    <button ion-button block type="submit" [disabled]="!loginForm.valid">
      Login
    </button>

  </form>

  <button ion-button block clear (click)="goToSignup()">
    Create a new account
  </button>

  <button ion-button block clear (click)="goToResetPassword()">
    I forgot my password
  </button>

</ion-content>

That’s an HTML form using Ionic components, there’s also some form validation going on (and we’ll add more form validation stuff to the TypeScript file) that will not be covered in the guide, but you can read about it in full detail in this post.

That .invalid class adds a red border to note that there’s something wrong with the field (you should add something more specific)

Open your login.scss and add the .invalid class:

page-login {
  .invalid {
    border-bottom: 1px solid #ff6153;
  }
}

And finally it’s time to jump into the actual TypeScript code, open your login.ts file, you should have something similar to this:

import { Component } from '@angular/core';
import { IonicPage, NavController } from 'ionic-angular';

@IonicPage({
  name: 'login'
})
@Component({
  selector: 'page-login',
  templateUrl: 'login.html',
})
export class LoginPage {

  constructor(public navCtrl: NavController) {...}
}

The first thing we’ll add is the imports, so everything is available when you need it:

import { Component } from '@angular/core';
import {
  IonicPage,
  Loading,
  LoadingController,
  NavController,
  AlertController,
} from 'ionic-angular';
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import { EmailValidator } from '../../validators/email';
import { AuthProvider } from '../../providers/auth/auth';
import { HomePage } from '../home/home';

This is the breakdown of what we’re importing:

  • Loading and FormGroup because I’m a TypeScript junkie, we’ll use those to declare the types of the loading component’s variable and the form variable.
  • LoadingController and AlertController because we’re going to be using an alert pop up and a loading component inside our page.
  • FormBuilder, Validators those are used to get form validation going on with angular.
  • AuthProvider is the authentication provider we created, we’ll be using it to call the login function.
  • EmailValidator is a custom validator I created, it makes sure that when the user is going to type an email address, it follows this format: something @ something . something.

Let’s get that out of the way right now, create a file: src/validators/email.ts and populate it with the following:

import { FormControl } from '@angular/forms';

export class EmailValidator {
  static isValid(control: FormControl) {
    const re = /^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/.test(
      control.value
    );

    if (re) {
      return null;
    }

    return {
      invalidEmail: true,
    };
  }
}

Explaining angular validators is out of the scope of this post, but you can check this post that goes into more detail.

After everything is imported, we’re going to inject all the providers in the constructor, so they become available inside the class:

constructor(
  public navCtrl: NavController,
  public loadingCtrl: LoadingController,
  public alertCtrl: AlertController,
  public authProvider: AuthProvider,
  public formBuilder: FormBuilder
) {}

While we’re at it, we’re going to create two global variables for this class right before the constructor, one to handle our login form, and the other one to handle our loading component:

public loginForm: FormGroup;
public loading: Loading;
constructor(
  public navCtrl: NavController,
  public loadingCtrl: LoadingController,
  public alertCtrl: AlertController,
  public authProvider: AuthProvider,
  public formBuilder: FormBuilder
) {}

Inside the constructor, we need to initialize our form:

constructor(
  public navCtrl: NavController,
  public loadingCtrl: LoadingController,
  public alertCtrl: AlertController,
  public authProvider: AuthProvider,
  public formBuilder: FormBuilder
) {
    this.loginForm = formBuilder.group({
      email: ['',
      Validators.compose([Validators.required, EmailValidator.isValid])],
      password: ['',
      Validators.compose([Validators.minLength(6), Validators.required])]
    });
}

We are using formBuilder inside the constructor to initialize the fields and give them a required validator.

If you want to know more about FormBuilder check Angular’s docs.

Now let’s create our functions:

loginUser(): void {
  if (!this.loginForm.valid){
    console.log(this.loginForm.value);
  } else {
    this.authProvider.loginUser(this.loginForm.value.email,
      this.loginForm.value.password)
    .then( authData => {
      this.loading.dismiss().then( () => {
        this.navCtrl.setRoot(HomePage);
      });
    }, error => {
      this.loading.dismiss().then( () => {
        let alert = this.alertCtrl.create({
          message: error.message,
          buttons: [
            {
              text: "Ok",
              role: 'cancel'
            }
          ]
        });
        alert.present();
      });
    });
    this.loading = this.loadingCtrl.create();
    this.loading.present();
  }
}

Our login function takes the value of the form fields and passes them to our loginUser function inside our AuthProvider service.

It’s also calling Ionic’s loading component since the app needs to communicate with the server to log the user in there might be a small delay in sending the user to the HomePage so we are using a loading component to give a visual so the user can understand that it’s loading 😛

goToSignup(): void {
  this.navCtrl.push('SignupPage');
}

goToResetPassword(): void {
  this.navCtrl.push('ResetPasswordPage');
}

Those two should be easy to understand, and we are sending the user to the SignupPage or the ResetPasswordPage

The Password Reset Page

The first app (web app) I built didn’t have a password reset function, and I was building it with PHP without any frameworks, hacking around stuff while learning on the go.

So every time someone needed to reset their password they needed to email me so I could manually reset it and then send them to their email (that was bad!).

Firebase handles this very easy for us, we create a page where the user inputs the email address, and we call the reset password function we created in our authentication provider.

Go ahead and create the page:

$ ionic generate page ResetPassword
$ ionic generate page Signup

We created the SignupPage too, so we don’t have to open the terminal again 😛

We’re going to handle this the same way we did the login page (view, style, code), so open your reset-password.html file and create almost the same form we created for the login page, with the email field (don’t forget to change the form name!)

<ion-content padding>
  <form [formGroup]="resetPasswordForm" (submit)="resetPassword()" novalidate>

    <ion-item>
      <ion-label stacked>Email</ion-label>
      <ion-input formControlName="email" type="email" placeholder="Your email address"
        [class.invalid]="!resetPasswordForm.controls.email.valid && blur">
      </ion-input>
    </ion-item>

    <button ion-button block type="submit" [disabled]="!resetPasswordForm.valid">
      Reset your Password
    </button>

  </form>
</ion-content>

Add the .invalid class to the reset-password.scss file:

page-reset-password {
  .invalid {
    border-bottom: 1px solid #ff6153;
  }
}

And now it’s time to code the functionality, open reset-password.ts, it should look like this:

import { Component } from '@angular/core';
import { IonicPage, NavController } from 'ionic-angular';

@IonicPage({
  name: 'reset-password'
})
@Component({
  selector: 'page-reset-password',
  templateUrl: 'reset-password.html',
})
export class ResetPasswordPage {

  constructor(public navCtrl: NavController) {...}
}

Like we did in the login page, we are going to be adding the imports and injecting our services into the constructor:

import { Component } from '@angular/core';
import { IonicPage, NavController, AlertController } from 'ionic-angular';
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import { AuthProvider } from '../../providers/auth/auth';
import { EmailValidator } from '../../validators/email';

@IonicPage({
  name: 'reset-password'
})
@Component({
  selector: 'page-reset-password',
  templateUrl: 'reset-password.html',
})
export class ResetPasswordPage {
  public resetPasswordForm: FormGroup;

  constructor(
    public navCtrl: NavController,
    public authProvider: AuthProvider,
    public formBuilder: FormBuilder,
    public alertCtrl: AlertController
  ) {
      this.resetPasswordForm = formBuilder.group({
        email: ['',
        Validators.compose([Validators.required, EmailValidator.isValid])],
      });
  }
}

Then we create the password resetting function:

resetPassword(){
  if (!this.resetPasswordForm.valid){
    console.log(this.resetPasswordForm.value);
  } else {
    this.authProvider.resetPassword(this.resetPasswordForm.value.email)
    .then((user) => {
      let alert = this.alertCtrl.create({
        message: "We sent you a reset link to your email",
        buttons: [
          {
            text: "Ok",
            role: 'cancel',
            handler: () => { this.navCtrl.pop(); }
          }
        ]
      });
      alert.present();

    }, (error) => {
      var errorMessage: string = error.message;
      let errorAlert = this.alertCtrl.create({
        message: errorMessage,
        buttons: [{ text: "Ok", role: 'cancel' }]
      });
      errorAlert.present();
    });
  }
}

Same as login, it takes the value of the form field, sends it to the AuthProvider service and displays a loading component while we get Firebase’s response.

If there’s something about that file that you don’t understand, don’t hesitate to leave a comment below and I’ll be happy to help you.

The Signup Page

We’re missing our signup page. This is the page that will be used by new users to create a new account, and I’m going to be a bit more fast paced with this one (basically pasting the code) since it’s the same as the login page but changing the forms name.

The first thing we’ll create is the view, and this is how you want your signup.html to look like:

<ion-content padding>
  <form [formGroup]="signupForm" (submit)="signupUser()" novalidate>

    <ion-item>
      <ion-label stacked>Email</ion-label>
      <ion-input formControlName="email" type="email" placeholder="Your email address"
        [class.invalid]="!signupForm.controls.email.valid && blur">
      </ion-input>
    </ion-item>

    <ion-item>
      <ion-label stacked>Password</ion-label>
      <ion-input formControlName="password" type="password" placeholder="Your password"
        [class.invalid]="!signupForm.controls.password.valid && blur">
      </ion-input>
    </ion-item>

    <button ion-button block type="submit" [disabled]="!signupForm.valid">
      Create an Account
    </button>
  </form>
</ion-content>

Now add the .invalid class to the signup.scss file (You could just add it to the global CSS file and do it just once):

page-signup {
  .invalid {
    border-bottom: 1px solid #ff6153;
  }
}

And finally open your signup.ts file, import the services you’ll need and inject them to your constructor:

import { Component } from '@angular/core';
import { IonicPage,
  NavController,
  Loading,
  LoadingController,
  AlertController } from 'ionic-angular';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AuthProvider } from '../../providers/auth/auth';
import { EmailValidator } from '../../validators/email';
import { HomePage } from '../home/home';

@IonicPage({
  name: 'signup'
})
@Component({
  selector: 'page-signup',
  templateUrl: 'signup.html',
})
export class SignupPage {
  public signupForm: FormGroup;
  public loading: Loading;
  constructor(
    public navCtrl: NavController,
    public authProvider: AuthProvider,
    public formBuilder: FormBuilder,
    public loadingCtrl: LoadingController,
    public alertCtrl: AlertController
  ) {
      this.signupForm = formBuilder.group({
        email: ['',
          Validators.compose([Validators.required, EmailValidator.isValid])],
        password: ['',
          Validators.compose([Validators.minLength(6), Validators.required])]
      });
    }
}

You’ve done this twice now so there shouldn’t be anything new here, now create the signup function:

signupUser(){
  if (!this.signupForm.valid){
    console.log(this.signupForm.value);
  } else {
    this.authProvider.signupUser(this.signupForm.value.email,
      this.signupForm.value.password)
    .then(() => {
      this.loading.dismiss().then( () => {
        this.navCtrl.setRoot(HomePage);
      });
    }, (error) => {
      this.loading.dismiss().then( () => {
        let alert = this.alertCtrl.create({
          message: error.message,
          buttons: [
            {
              text: "Ok",
              role: 'cancel'
            }
          ]
        });
        alert.present();
      });
    });
    this.loading = this.loadingCtrl.create();
    this.loading.present();
  }
}

And there you have it. You have a fully functional auth system working on your app now.

A piece of advice, store what you have right now in a Github repository, you’ll be able to clone it every time you need to start an app that uses Firebase as an authentication backend, saving tons of time.

This lesson was long, go for a walk, grab a cookie, or whatever you want to do for fun.

If you want to take a deeper dive on Ionic + Firestore you should go through my FREE course: Build your first Firestore powered Ionic app.

Related

Filed Under: Ionic & Firebase Tagged With: Authentication, Firebase JavaScript Web SDK, Realtime Database

Copyright © 2019 · Genesis Sample on Genesis Framework · WordPress · Log in