Full Angular 7|8|9|10 Firebase Authentication Tutorial Examples

Last updated on by Digamber
In this Angular tutorial, we are going to build full Angular 7|8|9|10 Firebase Authentication system from scratch using Firebase Real-time NoSQL cloud database.
This tutorial helps you cover the following topics:

  • Sign in with Google
  • Sign in with username/password
  • Sign up with email/password
  • Recover forget password
  • Send email verification to a newly created user
  • Protect or secure inner pages routes using CanActivate guard
  • Restrict access of non-authenticated users
  • Manage logged in state of Firebase user with LocalStorage

Getting Started

Before we go ahead, make sure you have Node JS set up on your local development machine.

Install Angular CLI, Ignore if already installed.

npm install -g @angular/cli

Next, create Angular 10 application.

Use the given below cmd to setup the Angular project.

ng new angularfiebase-authentication

Once the project is downloaded, get into the project directory.

cd angularfirebase-authentication

Install Bootstrap CSS Framework in Angular 10 application.

npm install bootstrap

Go to angular.json file and replace the given below code with “styles”: [ ] array.

"styles": [
            "node_modules/bootstrap/dist/css/bootstrap.min.css",
            "src/styles.css"
          ]

Install AngularFire2 Package

I assume you have already created a Firebae project, if not then follow this tutorial How to setup a basic Firebase account and set up a Firebase Project?

Install AngularFire2 package in Angular 10 app.

npm install firebase @angular/fire --save

Add your firebase configuration in enviorment.ts and enviorment.prod.ts files.

export const environment = {
  production: false,
  firebase: {
    apiKey: "xxxxxxxx-xxxxxxxx",
    authDomain: "xxxxxxxxxxxxxxxxxxxxxxxx",
    databaseURL: "xxxxxxxxxxxxxxxxxxxxxxxx",
    projectId: "xxxxxxxx",
    storageBucket: "xxxxxxxx",
    messagingSenderId: "xxxxxx",
    appId: "xxxxx",
    measurementId: "xxxxxxxxxxxxxxxx"
  }
};

Import and register AngularFire2 modules in app.module.ts.

// Firebase services + enviorment module
import { AngularFireModule } from "@angular/fire";
import { AngularFireAuthModule } from "@angular/fire/auth";
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { environment } from '../environments/environment';


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

Generate Angular Components

In order to create a complete Angular 7|8|9|10 Firebase Authentication system, we need to generate angular components.

ng g c components/dashboard
ng g c components/sign-in
ng g c components/sign-up
ng g c components/forgot-password
ng g c components/verify-email

Create Angular Routes

Add following code in app-routing.module.ts file.

import { NgModule } from '@angular/core';
// Required services for navigation
import { Routes, RouterModule } from '@angular/router';

// Import all the components for which navigation service has to be activated 
import { SignInComponent } from '../../components/sign-in/sign-in.component';
import { SignUpComponent } from '../../components/sign-up/sign-up.component';
import { DashboardComponent } from '../../components/dashboard/dashboard.component';
import { ForgotPasswordComponent } from '../../components/forgot-password/forgot-password.component';
import { AuthGuard } from "../../shared/guard/auth.guard";
import { VerifyEmailComponent } from '../../components/verify-email/verify-email.component';

const routes: Routes = [
  { path: '', redirectTo: '/sign-in', pathMatch: 'full' },
  { path: 'sign-in', component: SignInComponent },
  { path: 'register-user', component: SignUpComponent },
  { path: 'dashboard', component: DashboardComponent },
  { path: 'forgot-password', component: ForgotPasswordComponent },
  { path: 'verify-email-address', component: VerifyEmailComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})

export class AppRoutingModule { }

Enable the routes within view, add the following code in app.component.html file.

<router-outlet></router-outlet>

Create Firebase Authentication Service

Generate auth service and user interface files to create a Firebase authentication system with Angular 7|8|9/10.

Create User Interface class.

ng generate interface shared/services/user

Go to shared/services/user.ts

This user interface class is a schema for User object.

export interface User {
   uid: string;
   email: string;
   displayName: string;
   photoURL: string;
   emailVerified: boolean;
}

Create Auth Service

This file holds the core logic of our authentication system. I’ll be covering up social login using Firebase’s Google auth provider. You can also create the login with Facebook, Twitter, and GitHub later on.

The auth service will cover the sign-in with username/password, sign-up with sign-in with email/password, password reset, email verification, route protection using canActivate auth guard method.

import { Injectable, NgZone } from '@angular/core';
import { User } from "../services/user";
import { auth } from 'firebase/app';
import { AngularFireAuth } from "@angular/fire/auth";
import { AngularFirestore, AngularFirestoreDocument } from '@angular/fire/firestore';
import { Router } from "@angular/router";

@Injectable({
  providedIn: 'root'
})

export class AuthService {
  userData: any; // Save logged in user data

  constructor(
    public afs: AngularFirestore,   // Inject Firestore service
    public afAuth: AngularFireAuth, // Inject Firebase auth service
    public router: Router,  
    public ngZone: NgZone // NgZone service to remove outside scope warning
  ) {    
    /* Saving user data in localstorage when 
    logged in and setting up null when logged out */
    this.afAuth.authState.subscribe(user => {
      if (user) {
        this.userData = user;
        localStorage.setItem('user', JSON.stringify(this.userData));
        JSON.parse(localStorage.getItem('user'));
      } else {
        localStorage.setItem('user', null);
        JSON.parse(localStorage.getItem('user'));
      }
    })
  }

  // Sign in with email/password
  SignIn(email, password) {
    return this.afAuth.auth.signInWithEmailAndPassword(email, password)
      .then((result) => {
        this.ngZone.run(() => {
          this.router.navigate(['dashboard']);
        });
        this.SetUserData(result.user);
      }).catch((error) => {
        window.alert(error.message)
      })
  }

  // Sign up with email/password
  SignUp(email, password) {
    return this.afAuth.auth.createUserWithEmailAndPassword(email, password)
      .then((result) => {
        /* Call the SendVerificaitonMail() function when new user sign 
        up and returns promise */
        this.SendVerificationMail();
        this.SetUserData(result.user);
      }).catch((error) => {
        window.alert(error.message)
      })
  }

  // Send email verfificaiton when new user sign up
  SendVerificationMail() {
    return this.afAuth.auth.currentUser.sendEmailVerification()
    .then(() => {
      this.router.navigate(['verify-email-address']);
    })
  }

  // Reset Forggot password
  ForgotPassword(passwordResetEmail) {
    return this.afAuth.auth.sendPasswordResetEmail(passwordResetEmail)
    .then(() => {
      window.alert('Password reset email sent, check your inbox.');
    }).catch((error) => {
      window.alert(error)
    })
  }

  // Returns true when user is looged in and email is verified
  get isLoggedIn(): boolean {
    const user = JSON.parse(localStorage.getItem('user'));
    return (user !== null && user.emailVerified !== false) ? true : false;
  }

  // Sign in with Google
  GoogleAuth() {
    return this.AuthLogin(new auth.GoogleAuthProvider());
  }

  // Auth logic to run auth providers
  AuthLogin(provider) {
    return this.afAuth.auth.signInWithPopup(provider)
    .then((result) => {
       this.ngZone.run(() => {
          this.router.navigate(['dashboard']);
        })
      this.SetUserData(result.user);
    }).catch((error) => {
      window.alert(error)
    })
  }

  /* Setting up user data when sign in with username/password, 
  sign up with username/password and sign in with social auth  
  provider in Firestore database using AngularFirestore + AngularFirestoreDocument service */
  SetUserData(user) {
    const userRef: AngularFirestoreDocument<any> = this.afs.doc(`users/${user.uid}`);
    const userData: User = {
      uid: user.uid,
      email: user.email,
      displayName: user.displayName,
      photoURL: user.photoURL,
      emailVerified: user.emailVerified
    }
    return userRef.set(userData, {
      merge: true
    })
  }

  // Sign out 
  SignOut() {
    return this.afAuth.auth.signOut().then(() => {
      localStorage.removeItem('user');
      this.router.navigate(['sign-in']);
    })
  }

}

After that, go to app.module.ts file and import authentication service and pass the AuthService class into providers: [AuthService] array. By doing this our authentication service will be available throughout the application.

// Auth service
import { AuthService } from "./shared/services/auth.service";

@NgModule({
  declarations: [...],
  imports: [...],
  providers: [AuthService],
  bootstrap: [...]
})

Create Angular Login with Firebase API

It’s time to use AuthService class, It will help us creating Login authentication in Angular with Firebase.

We will focus on:

  • Sign-in with Username and Password
  • Sign-in with Gmail or Google auth

We need to import AuthService into sign-in/sign-in.component.ts, then inject AuthService into the constructor.

import { Component, OnInit } from '@angular/core';
import { AuthService } from "../../shared/services/auth.service";

@Component({
  selector: 'app-sign-in',
  templateUrl: './sign-in.component.html',
  styleUrls: ['./sign-in.component.css']
})

export class SignInComponent implements OnInit {

  constructor(
    public authService: AuthService
  ) { }

  ngOnInit() { }

}

Place the following code inside the sign-in/sign-in.component.html file.

<div class="displayTable">
  <div class="displayTableCell">

    <div class="authBlock">
      <h3>Sign In</h3>
      <div class="formGroup">
        <input type="text" class="formControl" placeholder="Username" #userName required>
      </div>

      <div class="formGroup">
        <input type="password" class="formControl" placeholder="Password" #userPassword required>
      </div>

      <!-- Calling SignIn Api from AuthService -->
      <div class="formGroup">
        <input type="button" class="btn btnPrimary" value="Log in" (click)="authService.SignIn(userName.value, userPassword.value)">
      </div>

      <div class="formGroup">
        <span class="or"><span class="orInner">Or</span></span>
      </div>

      <!-- Calling GoogleAuth Api from AuthService -->
      <div class="formGroup">
        <button type="button" class="btn googleBtn" (click)="authService.GoogleAuth()">
          <i class="fab fa-google-plus-g"></i>
          Log in with Google
        </button>
      </div>

      <div class="forgotPassword">
        <span routerLink="/forgot-password">Forgot Password?</span>
      </div>
    </div>

    <div class="redirectToLogin">
      <span>Don't have an account?<span class="redirect" routerLink="/register-user"> Sign Up</span></span>
    </div>

  </div>
</div>

User Registration with Angular 10 Firebase

Now, we will register the user with Angular and Firebae.

Go to sign-up/sign-up.component.ts and add the code.

import { Component, OnInit } from '@angular/core';
import { AuthService } from "../../shared/services/auth.service";

@Component({
  selector: 'app-sign-up',
  templateUrl: './sign-up.component.html',
  styleUrls: ['./sign-up.component.css']
})

export class SignUpComponent implements OnInit {

  constructor(
    public authService: AuthService
  ) { }

  ngOnInit() { }

}

Go to sign-up/sign-up.component.html and add the code.

<div class="displayTable">
  <div class="displayTableCell">

    <div class="authBlock">
      <h3>Sign Up</h3>

      <div class="formGroup">
        <input type="email" class="formControl" placeholder="Email Address" #userEmail required>
      </div>

      <div class="formGroup">
        <input type="password" class="formControl" placeholder="Password" #userPwd required>
      </div>

      <div class="formGroup">
        <input type="button" class="btn btnPrimary" value="Sign Up" (click)="authService.SignUp(userEmail.value, userPwd.value)">
      </div>

      <div class="formGroup">
        <span class="or"><span class="orInner">Or</span></span>
      </div>

      <!-- Continue with Google -->
      <div class="formGroup">
        <button type="button" class="btn googleBtn" (click)="authService.GoogleAuth()">
          <i class="fab fa-google-plus-g"></i>
          Continue with Google
        </button>
      </div>

      <!-- Continue with Facebook -->
      <div class="formGroup">
        <button type="button" class="btn facebookBtn" (click)="authService.FacebookAuth()">
          <i class="fab fa-facebook"></i>
          Continue with Facebook
        </button>
      </div>
    </div>

    <div class="redirectToLogin">
      <span>Already have an account? <span class="redirect" routerLink="/sign-in">Log In</span></span>
    </div>
  </div>

</div>

Angular 10 Forgot Password with Firebase

We are going to create forgot password feature using Firebase in Angular.

Go to forgot-password.component.ts add the code.

import { Component, OnInit } from '@angular/core';
import { AuthService } from "../../shared/services/auth.service";

@Component({
  selector: 'app-forgot-password',
  templateUrl: './forgot-password.component.html',
  styleUrls: ['./forgot-password.component.css']
})

export class ForgotPasswordComponent implements OnInit {

  constructor(
    public authService: AuthService
  ) { }

  ngOnInit() {
  }

}

Go to forgot-password.component.html add the code.

<div class="displayTable">
  <div class="displayTableCell">
    <div class="authBlock">
      <h3>Reset Password</h3>

      <p class="text-center">Please enter your email address to request a password reset.</p>

      <div class="formGroup">
        <input type="email" class="formControl" placeholder="Email Address" #passwordResetEmail required>
      </div>

      <!-- Calling ForgotPassword from AuthService Api -->
      <div class="formGroup">
        <input type="submit" class="btn btnPrimary" value="Reset Password" (click)="authService.ForgotPassword(passwordResetEmail.value)">
      </div>
    </div>

    <div class="redirectToLogin">
      <span>Go back to ? <span class="redirect" routerLink="/sign-in">Log In</span></span>
    </div>

  </div>
</div>

Send Verification Email

Firebase allows us to send verification email easily, add code in verify-email/verify-email.component.ts.

import { Component, OnInit } from '@angular/core';
import { AuthService } from "../../shared/services/auth.service";

@Component({
  selector: 'app-verify-email',
  templateUrl: './verify-email.component.html',
  styleUrls: ['./verify-email.component.css']
})
export class VerifyEmailComponent implements OnInit {

  constructor(
    public authService: AuthService
  ) { }

  ngOnInit() {
  }

}

Go to src/app/components/verify-email/verify-email.component.html and include the code.

<div class="displayTable">
  <div class="displayTableCell">

    <div class="authBlock">
      <h3>Thank You for Registering</h3>

      <div class="formGroup" *ngIf="authService.userData as user">
        <p class="text-center">We have sent a confirmation email to <strong>{{user.email}}</strong>.</p>
        <p class="text-center">Please check your email and click on the link to verfiy your email address.</p>
      </div>
      
      <!-- Calling SendVerificationMail() method using authService Api -->
      <div class="formGroup">
        <button type="button" class="btn btnPrimary" (click)="authService.SendVerificationMail()">
          <i class="fas fa-redo-alt"></i>
          Resend Verification Email
        </button>
      </div>

    </div>

    <div class="redirectToLogin">
      <span>Go back to?<span class="redirect" routerLink="/sign-in"> Sign in</span></span>
    </div>

  </div>
</div>

Use Route Guards to Protect Angular Routes

Routes guards secure routes in Angular. Now, I will tell you how to easily secure routes from unauthorized access using canActivate() route guard method.

Head over to auth.service.ts and look for the isLoggedIn() method. This function returns the boolean result to true when the user is logged-in. If user is not found then it will return false and doesn’t allow users to access the desired pages.

// Returns true when user is looged in and email is verified
get isLoggedIn(): boolean {
  const user = JSON.parse(localStorage.getItem('user'));
  return (user !== null && user.emailVerified !== false) ? true : false;
}

We have to secure the inner pages, to get this functionality, we have to generate route guard files.

Execute command to create route guards.

ng generate guard shared/guard/auth

Go to auth.guard.ts and place the code.

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { AuthService } from "../../shared/services/auth.service";
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})

export class AuthGuard implements CanActivate {
  
  constructor(
    public authService: AuthService,
    public router: Router
  ){ }

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    if(this.authService.isLoggedIn !== true) {
      this.router.navigate(['sign-in'])
    }
    return true;
  }

}

We have successfully secured application routes, now user needs to be authenticated before accessing app’s inner pages.

Go to app-routing.module.ts and include route guard in angular routes.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { SignInComponent } from '../../components/sign-in/sign-in.component';
import { SignUpComponent } from '../../components/sign-up/sign-up.component';
import { DashboardComponent } from '../../components/dashboard/dashboard.component';
import { ForgotPasswordComponent } from '../../components/forgot-password/forgot-password.component';
import { VerifyEmailComponent } from '../../components/verify-email/verify-email.component';

import { AuthGuard } from "../../shared/guard/auth.guard";

const routes: Routes = [
  { path: '', redirectTo: '/sign-in', pathMatch: 'full'},
  { path: 'sign-in', component: SignInComponent},
  { path: 'register-user', component: SignUpComponent},
  { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
  { path: 'forgot-password', component: ForgotPasswordComponent },
  { path: 'verify-email-address', component: VerifyEmailComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})

export class AppRoutingModule { }

Manage Firebase User Authentication State with LocalStorage

This step walks you through on how to Manage Firebase User Authentication State using HTML LocalStorage API. Managing logged in user data in Local Storage is easy with Angular and Firebase.

We will save the user state in Local Storage when the user is logged in, user details will be available even if we refresh the page. Also, remove the user data from local storage if we log out from the app.

Go to auth.service.ts and add the code.

import { Injectable, NgZone } from '@angular/core';
import { User } from "../services/user";
import { auth } from 'firebase/app';
import { AngularFireAuth } from "@angular/fire/auth";
import { AngularFirestore, AngularFirestoreDocument } from '@angular/fire/firestore';
import { Router } from "@angular/router";

@Injectable({
  providedIn: 'root'
})

export class AuthService {
  userData: any; // Save logged in user data

  constructor(
    public afs: AngularFirestore,   // Inject Firestore service
    public afAuth: AngularFireAuth, // Inject Firebase auth service
    public router: Router,  
    public ngZone: NgZone // NgZone service to remove outside scope warning
  ) {    
    /* Saving user data in localstorage when 
    logged in and setting up null when logged out */
    this.afAuth.authState.subscribe(user => {
      if (user) {
        this.userData = user;
        localStorage.setItem('user', JSON.stringify(this.userData));
        JSON.parse(localStorage.getItem('user'));
      } else {
        localStorage.setItem('user', null);
        JSON.parse(localStorage.getItem('user'));
      }
    })
  }

  // Sign in with email/password
  SignIn(email, password) {
    return this.afAuth.auth.signInWithEmailAndPassword(email, password)
      .then((result) => {
        this.ngZone.run(() => {
          this.router.navigate(['dashboard']);
        });
        this.SetUserData(result.user);
      }).catch((error) => {
        window.alert(error.message)
      })
  }

  // Sign up with email/password
  SignUp(email, password) {
    return this.afAuth.auth.createUserWithEmailAndPassword(email, password)
      .then((result) => {
        /* Call the SendVerificaitonMail() function when new user sign 
        up and returns promise */
        this.SendVerificationMail();
        this.SetUserData(result.user);
      }).catch((error) => {
        window.alert(error.message)
      })
  }

  // Sign in with Google
  GoogleAuth() {
    return this.AuthLogin(new auth.GoogleAuthProvider());
  }  

  // Auth logic to run auth providers
  AuthLogin(provider) {
    return this.afAuth.auth.signInWithPopup(provider)
    .then((result) => {
       this.ngZone.run(() => {
          this.router.navigate(['dashboard']);
        })
      this.SetUserData(result.user);
    }).catch((error) => {
      window.alert(error)
    })
  }

  /* Setting up user data when sign in with username/password, 
  sign up with username/password and sign in with social auth  
  provider in Firestore database using AngularFirestore + AngularFirestoreDocument service */
  SetUserData(user) {
    const userRef: AngularFirestoreDocument<any> = this.afs.doc(`users/${user.uid}`);
    const userData: User = {
      uid: user.uid,
      email: user.email,
      displayName: user.displayName,
      photoURL: user.photoURL,
      emailVerified: user.emailVerified
    }
    return userRef.set(userData, {
      merge: true
    })
  }

  // Sign out 
  SignOut() {
    return this.afAuth.auth.signOut().then(() => {
      localStorage.removeItem('user');
      this.router.navigate(['sign-in']);
    })
  }

}

We passed the user data in localStorage.setItem() method and getting the data from the localStorage.getItem('user') method.

Inject Angular Service in dashboard.component.html to take the benefit of it.

<!-- Top navigation -->
<nav class="navbar navbar-dark fixed-top bg-dark flex-md-nowrap p-0 shadow">
  <a class="navbar-brand col-sm-3 col-md-2 mr-0">
    <img class="brand-logo" src="assets/logo-positronx-white.svg" alt="positronX.io Logo">
  </a>
</nav>

<!-- Sidebar navigation -->
<div class="container-fluid">
  <div class="row">

    <nav class="col-md-2 d-md-block bg-light sidebar">
      <div class="sidebar-sticky">
        <ul class="nav flex-column">
          <li class="nav-item">
            <a class="nav-link active">
              <i class="fas fa-user"></i>User Profile
            </a>
          </li>
          <!-- Calling SignOut() Api from AuthService -->
          <li class="nav-item">
            <a class="nav-link" (click)="authService.SignOut()">
              <i class="fas fa-sign-out-alt"></i>Log out
            </a>
          </li>
        </ul>
      </div>
    </nav>

    <!-- Main content -->
    <main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
      <div class="inner-adjust">

        <div class="pt-3 pb-2 mb-3 border-bottom">
          <h1 class="h2">User Profile</h1>
        </div>
        <!-- Show user data when logged in -->
        <div class="row" *ngIf="authService.userData as user">
          <div class="col-md-12">
            <div class="media">
              <img class="align-self-start mr-5 img-thumbnail rounded-circle" src="{{(user.photoURL) ? user.photoURL : '/assets/dummy-user.png'}}"
                alt="{{user.displayName}}">
              <div class="media-body">
                <h1>Hello: <strong>{{(user.displayName) ? user.displayName : 'User'}}</strong></h1>
                <p>User ID: <strong>{{user.uid}}</strong></p>
                <p>Email: <strong>{{user.email}}</strong></p>
                <p>Email Verified: <strong>{{user.emailVerified}}</strong></p>
              </div>
            </div>
          </div>
        </div>

      </div>
    </main>

  </div>
</div>

Start the Angular authentication project in the browser.

ng serve --open

The below screenshot gives you the rough idea of how it will look on the browser.

Logged in State of Firebase User in localStorage

Lastly you can download the complete code of this tutorial from GitHub. I hope you liked this tutorial, please consider it sharing with others.