×
Sanjay Toge
Software Engineer
Sanjay Toge is a full stack developer who solves complex problems while creating some of the most advanced applications in the industry. He has ... Read More

Version

Angular CLI: 15.1.6

Node: 18.12.1

Package Manager: npm 9.5.0

It’s a good day to dive deep into Firebase authentication with Angular!

Streamline your front-end journey by selecting the ideal platform from Angular, React, and Vue.

To authenticate users for our Angular project, Firebase offers backend services, simple SDKs, and ready-to-use UI libraries. First, let’s look at the providers.

Providers

Passwords, phone numbers, well-known federated identity providers like Google, Facebook, and Twitter, among other methods, are all supported for authentication by Firebase.

Get started with Firebase Auth Nitor Infotech

Fig 1: Get started with Firebase Auth

npm Packages

Note: Skip these steps if you want to add Firebase authentication to your existing Angular app.

Now, how do we create Angular from scratch?

Creating Angular from Scratch

Make sure Node JS is installed on your local development machine before continuing.

Install Angular CLI; disregard if previously done so.

npm install -g angular/cli

Creating Angular app

ng new angular-firebase-authentication

Once the packages are downloaded and ready to use, get into the directory.

Let’s add Prime NG library to enhance our UI design.

npm install primeng primeicons

Add these css files to your angular.json file under “styles”

...
"styles": [
"node_modules/primeicons/primeicons.css",
"node_modules/primeng/resources/themes/lara-light-blue/theme.css",
"node_modules/primeng/resources/primeng.min.css",
...
]

Now, let’s install Firebase.

Installing Firebase

To use Firebase in our app, we will be using Firebase and @angular/fire.

Once the packages are installed, we can add the firebase configurations in our environment.ts file or get details from vault as per the security of the app.

If you do not see any environments file/folder, this might be as Angular 15 simply doesn’t ship environment files anymore by default.

You can still generate them using the command:

ng generate environments

You can get the configuration details from your Firebase account.

Once you have created your Project on the Firebase console you can go to:

Installing Firebase Nitor Infotech

Fig 2: Installing Firebase

Project Overview >> Setting icon >> Project Settings

Once you are in Project settings under General, you can find your app which will have a SDK setup and configurations.

SDK setup and configuration Nitor Infotech

Fig 3: SDK setup and configuration

Scroll down and you will find the firebaseConfigurations.

firebaseConfig: {
        apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
        authDomain: "app-name.firebaseapp.com",
        projectId: "app-name",
        storageBucket: "app-name.appspot.com",
        messagingSenderId: "000000000000",
        appId: "1:000000000000:web:012121212121212121",
        measurementId: "G-XXXXXXXX"
}

Once you have imported the configurations in your environment file, you need to Import and register Firebase modules in app.module.ts.

 imports: [
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFireAuthModule,
    AngularFirestoreModule,
    AngularFireStorageModule,
    AngularFireDatabaseModule,
]

Now it’s time to create components and routes.

Create Angular Components and Routes

Once you have imported and initialized the firebase module, let us create some components, routes and Authentication service.

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

Add the corresponding routes in your app.routing.module.ts or the specific module where you have created the components. It is better to use a separate module for authentication and authorization services. Now for simplicity we will add the routes in our app.routing.module.ts file.

const routes: Route = [
  { 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 },
];

Time for the Firebase Authentication Service!

Firebase Authentication Service

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

To the User interface, add the Object which is the users Schema.

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

The created auth service holds the logic of our Authentication system. We will integrate Firebase’s Google Authentication, similarly other providers can be configured.

The auth service will take care of the canActivate auth guard mechanism for route protection as well as sign-in with username/password, sign-up with email/password, password reset, and email verification.

We now need to Inject Firestore service, Firebase auth service, NgZone to remove outside scope warnings.

In the auth service configure these functions to sign in the user, signup, send the verification email, to reset the forgotten password, get user logged in status, Google auth, auth login to use providers, set users data, and sign out.

import { Injectable, NgZone } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/compat/auth';
import {
  AngularFirestore,
  AngularFirestoreDocument,
} from '@angular/fire/compat/firestore';
import { Router } from '@angular/router';
import { User } from './user';
import { GoogleAuthProvider } from 'firebase/auth';
@Injectable({
  providedIn: 'root',
})
export class AuthService {
  userData: User;
  constructor(
    public afAuth: AngularFireAuth,
    public afs: AngularFirestore,
    public ngZone: NgZone,
    public router: Router
  ) {
    /* 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 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']);
    });
  }
}

Once the code is added, go to your app.module.ts file and import authentication service so that it will be accessible throughout the application.

  providers:[
    AuthService
  ]

Now, let’s figure out the login component.

Login

Let’s use our AuthService in the Login Component.

Add this code to your Login ts file, in this file we have implemented a function from our Authservice which takes email and password as the arguments.

export class LoginComponent implements OnInit {
  loginForm: FormGroup;
  constructor(private router: Router, private authService: AuthService){
  }
  ngOnInit(): void {
    this.createLoginForm();
  }
  createLoginForm(){
    this.loginForm = new FormGroup({
      email: new FormControl('',Validators.required),
      password: new FormControl('',Validators.required)
    })
  }
  login(){
    if(this.loginForm.valid){
      this.authService.SignIn(this.loginForm.value.email, this.loginForm.value.password)
    }
  }
}

Add this code in your HTML file.

Here we have added the formgroup and the required fields.

<form [formGroup]="loginForm">
  <div class="grid">
    <div class="col-12">
      <div class="card flex justify-content-center">
        <p-card header="Login" [style]="{ width: '360px' }">
          <!-- <ng-template pTemplate="header">
                     </ng-template> -->
          <ng-template pTemplate="content">
            <div>
              <label for="username">Email</label>
              <div>
                <input
                  class="w-100"
                  pInputText
                  formControlName="email"
                  type="text"
                  placeholder="Email"
                />
              </div>
            </div>
            <div class="mt-3">
              <label for="password">Password</label>
              <div>
                <input
                  class="w-100"
                  pInputText
                  type="password"
                  formControlName="password"
                  placeholder="Password"
                />
              </div>
            </div>
          </ng-template>
          <ng-template pTemplate="footer">
            <div class="flex justify-content-center align-items-center">
              <button pButton pRipple label="Log In" (click)="login()"></button>
              <p-button
                label="Forgot Password?"
                styleClass="p-button-link"
                routerLink="/reset-password"
              ></p-button>
            </div>
            <div class="mt-2 flex justify-content-center align-items-center">
              <p-button
                label="Don't have an account?"
                styleClass="p-button-link"
                routerLink="/sign-up"
              ></p-button>
            </div>
          </ng-template>
        </p-card>
      </div>
    </div>
  </div>
</form>

Signup

Now to sign up a new user we will have to similarly inject the AuthService.

We have added a signup function which calls the signup function from our AuthService file.

export class SignUpComponent implements OnInit {
  signupForm: FormGroup;
  constructor(private authService: AuthService) {}
  ngOnInit(): void {
    this.createForm();
  }
  createForm() {
    this.signupForm = new FormGroup({
      email: new FormControl('', Validators.required),
      password: new FormControl('', Validators.required),
      confirmPassword: new FormControl('', Validators.required),
      displayName: new FormControl('', Validators.required),
    });
  }
  signup() {
    if (this.signupForm.valid) {
      this.authService.SignUp(
        this.signupForm.value.email,
        this.signupForm.value.password
      );
    }
  }
}

HTML code to show sign up form

<form [formGroup]="signupForm">
  <div class="grid">
    <div class="col-12">
      <div class="card flex justify-content-center">
        <p-card header="Sign up" [style]="{ width: '360px' }">
          <!-- <ng-template pTemplate="header">
             </ng-template> -->
          <ng-template pTemplate="content">
            <div>
              <label for="username">Name</label>
              <div>
                <input
                  class="w-100"
                  pInputText
                  formControlName="displayName"
                  type="text"
                  placeholder="Name"
                />
              </div>
            </div>
            <div class="mt-3">
              <label for="username">Email</label>
              <div>
                <input
                  class="w-100"
                  pInputText
                  formControlName="email"
                  type="text"
                  placeholder="Email"
                />
              </div>
            </div>
            <div class="mt-3">
              <label for="password">Password</label>
              <div>
                <input
                  class="w-100"
                  pInputText
                  type="password"
                  formControlName="password"
                  placeholder="Password"
                />
              </div>
            </div>
            <div class="mt-3">
              <label for="password">Confirm Password</label>
              <div>
                <input
                  class="w-100"
                  pInputText
                  type="password"
                  formControlName="confirmPassword"
                  placeholder="Confirm Password"
                />
              </div>
            </div>
          </ng-template>
          <ng-template pTemplate="footer">
            <div class="flex justify-content-center align-items-center">
              <button
                pButton
                pRipple
                label="Sign up"
                (click)="signup()"
              ></button>
            </div>
            <div class="mt-2 flex justify-content-center align-items-center">
              <p-button
                label="Already have an account? Click Here"
                styleClass="p-button-link"
                routerLink="/login"
              ></p-button>
            </div>
          </ng-template>
        </p-card>
      </div>
    </div>
  </div>
</form>

How do we reset the password?

Reset Password

To reset the password, send an email with a link so that the user only can reset the email link. This functionality is also already added in the auth service.

export class ResetPasswordComponent implements OnInit {
  resetForm: FormGroup;
  constructor(private authService: AuthService) {}
  ngOnInit(): void {
    this.resetForm = new FormGroup({
      email: new FormControl('', Validators.required),
    });
  }
  sendResetLink() {
    if (this.resetForm.valid) {
      this.authService.ForgotPassword(this.resetForm.value.email);
    }
  }
}

UI to enter email to send the link to reset password

<form [formGroup]="resetForm">
  <div class="grid">
    <div class="col-12">
      <div class="card flex justify-content-center">
        <p-card header="Login" [style]="{ width: '360px' }">
          <!-- <ng-template pTemplate="header">
             </ng-template> -->
          <ng-template pTemplate="content">
            <div>
              <label for="username">Email</label>
              <div>
                <input
                  class="w-100"
                  pInputText
                  formControlName="email"
                  type="text"
                  placeholder="Email"
                />
              </div>
            </div>
          </ng-template>
          <ng-template pTemplate="footer">
            <div class="flex justify-content-center align-items-center">
              <button
                pButton
                pRipple
                label="Reset Password"
                (click)="sendResetLink()"
              ></button>
            </div>
            <div class="mt-2 flex justify-content-center align-items-center">
              <p-button
                label="Don't have an account? Sign Up here!"
                styleClass="p-button-link"
                routerLink="/sign-up"
              ></p-button>
            </div>
            <div class="mt-2 flex justify-content-center align-items-center">
              <p-button
                label="Already have an account? Login Here"
                styleClass="p-button-link"
                routerLink="/login"
              ></p-button>
            </div>
          </ng-template>
        </p-card>
      </div>
    </div>
  </div>
</form>

Let’s explore the use of route guards.

Using Route Guards

In Angular, route guards protect the routes.

Now I’ll explain how to use the canActivate() route guard mechanism to quickly secure routes from unwanted access.

Let’s create our guard by using the command ng generate guard

ng generate guard shared/guard/auth
Select CanActivate()

Let’s look at our isLoggedIn() method, which returns boolean if the users object is stored in localStorage

// 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;
  }

In our auth file in the canActivate() check if user is logged in and if no, then return user to the login-page and:

 constructor(private authService: AuthService, public router: Router) {}
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ):
    | Observable<boolean | UrlTree>
    | Promise<boolean | UrlTree>
    | boolean
    | UrlTree {
    if (!this.authService.isLoggedIn) {
      this.router.navigate(['']);
    }
    return true;
  }

In our app-routing.module.ts file add canActivate to our route whose access we want to give to our authenticated users.

{ path: '', loadChildren: () => AuthenticateModule,  canActivate: [AuthGuard]  },

There you have it! That’s how one goes about Firebase authentication with Angular! I’m sure you will enjoy executing this tutorial blog.

Send us an email with your thoughts about this blog. Visit us at Nitor Infotech to know about the software we craft.

subscribe image

Subscribe to our
fortnightly newsletter!

we'll keep you in the loop with everything that's trending in the tech world.

We use cookies to ensure that we give you the best experience on our website. If you continue to use this site we will assume that you are happy with it. Accept Cookie policy