How to Implement Firebase Authentication in Angular 16

Last Updated on by in Angular Tutorial Firebase Tutorial

Authentication ensures whether a user is authorized to access the system or its resources. Auth is crucial for protecting user data, preventing unauthorized access to sensitive information, and bolstering trust among users.

In this comprehensive tutorial, we will walk you through a series of steps on how to create an advanced authentication system using Firebase SaaS database and Angular framework.

Angular, free front-end framework used to build dynamic single-page web applications, whereas Firebase is a comprehensive platform that offers a wide array of tools and services to boost up web development process.

Google Firebase offers a useful authentication features, such as: multiple sign-in methods, secure user authentication, email and password authentication, phone number authentication, social media logins, a custom authentication system, token-based authentication, User management and access control, identity providers, and OAuth. To know more about Firebase Authentication, make sure to check official Firebase documentation.

Advance Angular Firebase Authentication Example

By utilizing these Firebase auth methods, we will teach new web developers, how to implement complete Firebase authentication in Angular, how to enable auth services in the Firebase backend, and how to create complex authentication functionalities from scratch.

Here is the full list of Angular Firebase auth features that we will cover in this guide. By the end of this tutorial, you will know, How-to:

  • Build a user login and sign in using a username and password.
  • Build user registration using an email and password.
  • Create functionality to recover lost passwords.
  • Restrict access to unauthorized users in an Angular web application.
  • Send a verification email to the user’s email address after the new account is created or new signup.
  • Protect or secure the inner page’s private routes with the help of the CanActivate route guard.
  • Manage the state of the logged-in user in the browser’s memory using the LocalStorage mechanism.

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

Get into the project directory.

cd angularfirebase-authentication

Remove Angular TypeScript Errors

To get rid from the errors:

Property ‘xxxName’ comes from an index signature, so it must be accessed with [‘xxxName’]

This setting makes sure profound consistency between accessing a field via the “dot” (obj.key) syntax, and “indexed” (obj["key"]) and the way which the property is declared in the type.

Without this flag, TypeScript will allow you to use the dot syntax to access fields which are not defined:

Make sure to set following properties to false in tsconfig.json file:

"compilerOptions": {
 ...
 ...
   "strict": false,
   "noPropertyAccessFromIndexSignature": false,
 ...
 ...
},
  "angularCompilerOptions": {
    "strictTemplates": false
  }

Install Bootstrap CSS Framework in Angular 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.scss"
          ]

Set Up Firebase Packages

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

Install Firebase packages in Angular app.

npm install @angular/fire firebase@9.16.0 --legacy-peer-deps

Once you are done setting up this library, make the connection between your Firebase account and your Angular app.

Create src/environments/ folder and environment.ts file, also add firebase configurations in environments/environment.ts file.

export const environment = {
  production: false,
  firebase: {
    apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    authDomain: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    projectId: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    storageBucket: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    messagingSenderId: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    appId: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  }
};

Import and register firebase modules in 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,
  ]
})

Generate Angular Components

In order to create a complete Angular 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

Inside the src/app/ directory create app-routing.module.ts file and add the following code into the 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';
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.

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

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

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.

Update code in app shared/services/auth.service.ts file.

import { Injectable, NgZone } from '@angular/core';
import { User } from '../services/user';
import {
  AngularFirestore,
  AngularFirestoreDocument,
} from '@angular/fire/compat/firestore';
import * as auth from 'firebase/auth';
import { AngularFireAuth } from '@angular/fire/compat/auth';
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;
  }
  /* 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']);
    });
  }
}

Create Angular Login with Firebase API

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.scss']
})
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>
      <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 will register the user with Angular and Firebase.

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.scss']
})
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>
    <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

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.scss']
})
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.scss']
})
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.

Head over to auth.service.ts and look for the isLoggedIn() method.

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']);
    });
  }
}

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 {
  ActivatedRouteSnapshot,
  RouterStateSnapshot,
  Router,
  UrlTree,
} from '@angular/router';
import { AuthService } from '../../shared/services/auth.service';
import { Observable } from 'rxjs';
@Injectable({
  providedIn: 'root',
})
export class AuthGuard {
  constructor(public authService: AuthService, public router: Router) {}
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | Promise<boolean> | UrlTree | 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.

Next, open the app-routing.module.ts file, import the route guard in 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

Manage logged in user data with Local Storage in Angular with Firebase.

Save the user state in Local Storage when the user is logged in, user details will be available even if we refresh the page.

Therefore, head over to dashboard.component.html file and add the given code into the file.

<!-- 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, add the auth service class in 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

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

Logged in State of Firebase User in localStorage

Project Code

If you want to grab the complete code, you can download the code from from GitHub.

Conclusion

The combination of Angular and Firebase empowers frontend developers to build scalable, real-time, and secure web applications that free them from backend complexities.

Firebase Authentication serves as a powerful tool for frontend and backend developers equally, it simplifies the user authentication process while developing web and mobile applications.

In this comprehensive tutorial, we have learned create authentication system in Angular using Firebase authentication services.

❤️ Checkout Popular CSS Collection

Digamber - Author positronX.io

Digamber, the founder of PositronX, is a certified web developer with over 10 years of experience. He possesses a curiosity for learning new things and enjoys playing cricket on his days off.