We'll show you how to create an Angular 13 Firebase authentication system from scratch using the Firebase Real-time NoSQL cloud database in this article. 

The following topics are covered in this tutorial:

  • 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

Table of Content

  1. Getting Started
  2. Install Firebase Packages
  3. Generate Angular Components
  4. Create Routes
  5. Create Authentication Service
  6. Create Angular Sign-in with Firebase API
  7. Build User Registration
  8. Build Forgot Password
  9. Send Verification Email
  10. Secure Route with CanActivate
  11. Manage User Authentication State

Getting Started

Make sure you have Node JS installed on your local development workstation before we begin.

Ignore if Angular CLI is already installed.

npm install -g @angular/cli

Create an angular application next.

To set up the Angular project, use the command below.

ng new angularfiebase-authentication

Get into the project directory once the project has been downloaded.

cd angularfirebase-authentication

In an Angular application, install the Bootstrap CSS Framework.

npm install bootstrap

Replace the following code in the angular.json file with "styles": Array []

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

Set Up Firebase Packages

I'm assuming you've previously set up a Firebase project. In your Angular app, install the Firebase packages.

npm install firebase @angular/fire

In the enviorment.ts files, add your firebase settings.

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

In app.module.ts, import and register firebase modules.

// 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,
  ]
})

Generate Angular Components

We need to produce angular components in order to create a complete Angular 13 Firebase Authentication solution.

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

Create the app-routing.module.ts file in the src/app/ directory and add the following 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 {}

Add the following code to the app.component.html file to enable routes within view.

<router-outlet></router-outlet>

Create Firebase Authentication Service

Create a Firebase authentication system with Angular by generating auth service and user interface files.

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

Go to shared/services/user.ts

This user interface class is a User object schema.

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

Create Auth Service

This file contains the authentication system's essential logic. Using Firebase's Google auth provider, I'll hide social login. Later on, you can create a login with Facebook, Twitter, or GitHub.

Sign-in with username/passwordsign-up with email/passwordpassword resetemail verification, and route protection using the canActivate auth guard technique are all covered by the auth service.

Change the code in the shared/services/auth.service.ts file in the app.

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.ngZone.run(() => {
          this.router.navigate(['dashboard']);
        });
        this.SetUserData(result.user);
      })
      .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) => {
      if (res) {
        this.router.navigate(['dashboard']);
      }
    });
  }
  // Auth logic to run auth providers
  AuthLogin(provider: any) {
    return this.afAuth
      .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: 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, import authentication service and pass the AuthService class into the providers: [AuthService] array in the app.module.ts file. Our authentication service will be available throughout the application as a result of this.

// 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 the AuthService class, which will assist us in building Firebase login authentication in Angular.

We'll concentrate on:

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

We must first import AuthService into sign-in/sign-in.component.ts before injecting it into the function Object() { [native code] }.

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() { }
}

In the sign-in/sign-in.component.html file, paste the following code.

<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 Firebase

Now we'll set up Angular and Firebase for the user.

Add the code to sign-up/sign-up.component.ts.

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() { }
}

Add the code to sign-up/sign-up.component.html.

<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>

Angular Forgot Password with Firebase

Using Firebase and Angular, we'll construct a forgotten password functionality.

Add the code to forgot-password.component.ts.

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() {
  }
}

Add the code to forgot-password.component.html.

<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

We can easily send verification emails using Firebase by adding code to 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.scss']
})
export class VerifyEmailComponent implements OnInit {
  constructor(
    public authService: AuthService
  ) { }
  ngOnInit() {
  }
}

Include the code in src/app/components/verify-email/verify-email.component.html.

<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

In Angular, route guards protect routes. Now I'll show you how to use the canActivate() route guard mechanism to simply secure routes from unwanted access.

Look for the isLoggedIn() method in the auth.service.ts file. When the user is logged in, this function sets the boolean result to true. If the user cannot be discovered, the function returns false, preventing users from accessing the specified 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 need to safeguard the inner pages, and in order to do so, we'll need to create route guard files.

To create route guards, run the command.

ng generate guard shared/guard/auth

Fill in the code at auth.guard.ts.

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've successfully secured application routes; now, before accessing the app's inner pages, the user must be authenticated.

After that, open app-routing.module.ts and import the route guard into 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 {}

Manage Firebase User Authentication State with LocalStorage

In the auth service class, we've already written the code for handling user authentication status using the LocalStorage API. We'll teach you how to manage logged in user data with Local Storage in Angular with Firebase in a moment.

Save the current status of the user in Local Storage. Even if we reload the page when the user is logged in, the user details will be available. Also, when we log out of the app, erase the user info from local storage.

As a result, open the dashboard.component.html file and paste the code into 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>

Then, in the dashboard.component.ts file, add the auth service class.

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 {}
}

In the browser, open the Angular authentication project.

ng serve --open

I hope you will like the content and it will help you to learn Full Angular 13 Firebase Authentication Tutorial Example
If you like this content, do share.


Recommended Posts

View All

Advanced Techniques for Optimizing Angular Performance


Learn advanced techniques for optimizing Angular performance, including lazy loading, AOT compilation, change detection strategies, caching strategies...

Dependency Injection in Angular with example


Dependency injection, a well-known programming concept, is what separates a class from its dependencies

Building High-Quality User Interfaces with Angular Material Design


Learn how to build high-quality user interfaces using Angular Material Design. Follow these best practices for consistency, responsiveness, accessibil...

What is Angular CLI and how to do I use it?


Angular CLI is a powerful tool for developing Angular applications. Learn what it is and how to use it to streamline your development process.

TOP PROGRAMMING LANGUAGES USED IN POPULAR WEBSITES


A programming language is mainly used for developing apps for desktop and mobile devices.