Angular 14 Tutorial- Full Angular 14 firebase Authentication Tutorial example

Angular 14 Firebase Authentication Example; In this tutorial, we will learn how to build the Angular 14 Firebase Authentication System from scratch using the Firebase Real-time NoSQL cloud database.

This Angular tutorial is compatible with version 4+ including latest version 12, 11, 10, 9, 8 ,7, 6 & 5.

We will be covering the following topics in this Angular guide:

  • Sign In with google
  • Sign In with a Username/password
  • Sign Up with an 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-authentication users
  • Manage the logged-in state of Firebase user with LocalStorage

Now, we will learn all the above-mentioned topics step by step:

Step 1- Getting Started

First of all, we have to make sure that we already have Node Js set up on our local development system.

So, now, we have to install the Angular CLI:

npm install -g @angular/cli

 

Then, we will create an Angular app using the below command to set up the Angular project:

ng new angularfiebase-authentication

 

After downloading the project, we will get into the project directory:

cd angularfirebase-authentication

 

Further, we need to install the Bootstrap CSS framework in the Angular app:

npm install bootstrap

 

Then, we go to the angular.json file and replace the given code with “styles”: [] array:

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

 

Step 2- SetUp firebase Packages

After setting up the Firebase project, we will install the Firebase packages:

npm install firebase @angular/fire

Now, we have to add our Firebase configuration in the environment.ts file:

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

 

Here, we will import and register the Firebase modules in the app.module.ts:

// Firebase services + environment module
import { AngularFireModule } from '@angular/fire/compat';
import { AngularFireAuthModule } from '@angular/fire/compat/auth';
import { AngularFireStorageModule } from '@angular/fire/compat/storage';
import { AngularFirestoreModule } from '@angular/fire/compat/firestore';
import { AngularFireDatabaseModule } from '@angular/fire/compat/database';
import { environment } from '../environments/environment';

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

 

Step 3- Generate Angular Components

In order to create a complete Angular Firebase Authentication System, we have to generate the 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

 

Step 4- Create The Angular Routes

Here, we will create the app-routing.module.ts file inside the src/app/ directory and then add the below code to it:

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';
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 route within the view, and add the below code in the app.component.html file:

<router-outlet></router-outlet>

 

Step 5- Create Firebase Authentication Service

Afterward, we will generate the auth service and user interface files to create a Firebase Authentication System with Angular:

ng g i shared/services/user
ng g s shared/services/auth

 

Now, we go to the 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;
}

 

Step 6- Create Auth Service

This file holds the core logic of our authentication system. In this will be covered the social login using Firebase’s Google Auth provider. We 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 and route protection using the canActivate auth guard method.

Now, we need to update the code in the app shared/services/auth.service.ts file:

import { Injectable, NgZone } from '@angular/core';
import { User } from '../services/user';
import * as auth from 'firebase/auth';
import { AngularFireAuth } from '@angular/fire/compat/auth';
import {
  AngularFirestore,
  AngularFirestoreDocument,
} from '@angular/fire/compat/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: string, password: string) {
    return this.afAuth
      .signInWithEmailAndPassword(email, password)
      .then((result) => {
        this.SetUserData(result.user);
        this.afAuth.authState.subscribe((user) => {
          if (user) {
            this.router.navigate(['dashboard']);
          }
        });
      })
      .catch((error) => {
        window.alert(error.message);
      });
  }
  // Sign up with email/password
  SignUp(email: string, password: string) {
    return this.afAuth
      .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.currentUser
      .then((u: any) => u.sendEmailVerification())
      .then(() => {
        this.router.navigate(['verify-email-address']);
      });
  }
  // Reset Forggot password
  ForgotPassword(passwordResetEmail: string) {
    return this.afAuth
      .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()).then((res: any) => {
      this.router.navigate(['dashboard']);
    });
  }
  // Auth logic to run auth providers
  AuthLogin(provider: any) {
    return this.afAuth
      .signInWithPopup(provider)
      .then((result) => {
        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: any) {
    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.signOut().then(() => {
      localStorage.removeItem('user');
      this.router.navigate(['sign-in']);
    });
  }
}

After that, we go to the app.module.ts file and import the authentication service and pass the AuthService class into the 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: [...]
})

 

Step 7- Create Angular Login With Firebase API

Now, we will be using the AuthService class. It will help us create 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 onto sign-in/sign-in.components.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.scss']
})
export class SignInComponent implements OnInit {
  constructor(
    public authService: AuthService
  ) { }
  ngOnInit() { }
}

Here, we have to place the below 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>

 

Step 8- User Registration With Angular Firebase

In this step, we will register the user with Angular and Firebase.

We go to sign-up/sign-up.component.ts and add the below 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.scss']
})
export class SignUpComponent implements OnInit {
  constructor(
    public authService: AuthService
  ) { }
  ngOnInit() { }
}

Then, we will go to sign-up/sign-up.component.html and add the below code in it:

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

 

Step 9- Angular Forgot Password With Firebase

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

Now, we need to go to the forgot-password.component.ts to add the given 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.scss']
})
export class ForgotPasswordComponent implements OnInit {
  constructor(
    public authService: AuthService
  ) { }
  ngOnInit() {
  }
}

Here, we will go to the forgot-password.component.html to add the below 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>

 

Step 10- Send Verification Email

Firebase allows us to send verification emails easily. We simple have to add the code in verify-email/verify-email.components:

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.scss']
})
export class VerifyEmailComponent implements OnInit {
  constructor(
    public authService: AuthService
  ) { }
  ngOnInit() {
  }
}

further, we 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>

 

Step 11- Use Route Guard To Protect Angular Routes

Now, we will see how to protect routes from unauthorized access. For that, we will 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 the 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.

We will execute the command to create the route guards:

ng generate guard shared/guard/auth

Here, we have to go to auth.guards.ts and place the code in it:

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 the application routes. now, the user needs to be authenticated before accessing the app’s inner pages.

Then, we will open the app-route.module.ts file and import the route guard in the Angular routing file:

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';
// route guard
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 {}

 

Step 12- Manage firebase User Authentication State With LocalStorage

We have already set up the code for managing the user authentication state with the localStorage API in the auth service class.

Now, we have to 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, we have to remove the user data from the local storage if we log out from the app.

Therefore, we head over to the dashboard.component.html file and add the given code to 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>

Next, we add the auth service class in the dashboard.component.ts file:

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../shared/services/auth.service';
@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.scss'],
})
export class DashboardComponent implements OnInit {
  constructor(public authService: AuthService) {}
  ngOnInit(): void {}
}

 

Start the Angular authentication project in the browser:

ng serve --open

 

Conclusion

Hope that this Angular tutorial about Angular 14 firebase Authentication was a smooth and easy one for you to follow.

 

Thanks

Leave a Reply

Your email address will not be published. Required fields are marked *