Angular / Firebase / Authentication

This blog post is a little out of the ordinary since it is not related in any way to ServiceNow. Well except for the common technologies like html, css, and javascript.

In my spare time I like to develop websites using Angular, Ionic Framework and Firebase has hosting. I like Firebase a lot because so many features are tightly integrated like authentication and database.

So for a while I have been working on implementing authentication in a web application. I would like to be able to use Google and Facebook for login. I have seen several Firebase courses, read numerous tutorials on this subject. Common to all of them is that they are more than 1 year old and about a year ago a major change was made in Firebase so code examples are using outdated methods/modules etc and using AngularFire compat mode. I might be wrong about this but when I hear something is used in compatibility mode it means compromises and missing functionality. So I want to avoid using the compat versions of AngularFire.

Finally I succeeded in getting authentication to work with Angular 15, AngularFire 7.5 and Firebase 9.15. In this blog post I will describe the simplest web app that just adds a login and logout button. It will not include auth gaurds. At least not at this time. I might add this at a later stage.

Setup project

Login to Firebase console, create an account if you don’t have one. It is free and authentication is available within the free tier. Create a new project and in authentication enable the google provider. In this example I will only use use since it is easy to add more once you got the basic logic established.

Then install following

npm install -g @angular/cli
npm install -g @ionic/cli
npm install -g firebase-tools

Switch to a folder where you want your project to live and run this command, Choose Angular and blank template. You can say no to create account. The Ionic start command will create a new folder for the project.

ionic start auth-test
cd ./auth-test

Then it is time to add AngularFire. When this command runs you are asked about which project you want to use and what services you want to use. To start with just select Authentication. You can always add other services later.

ng add @angular/fire

At this point you can check if the app is working by running

ionic serve

Modify code

Now it is time to add the code for authentication

Run following commands to add login page and authentication service.

ionic g page login
ionic g service services/auth

Next open app.module.ts the code should look something like this

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { initializeApp, provideFirebaseApp } from '@angular/fire/app';
import { environment } from '../environments/environment';
import { provideAuth, getAuth } from '@angular/fire/auth';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    IonicModule.forRoot(),
    AppRoutingModule,
    provideFirebaseApp(() => initializeApp(environment.firebase)),
    provideAuth(() => getAuth())
  ],
  providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
  bootstrap: [AppComponent],
})
export class AppModule { }

Next open auth.service.ts the code should look like this, the service only sets up an observable to give the app the information wether logged in or not you can add more your self such as the full user detals and the token with claims etc.

import { Injectable } from '@angular/core';
import { Auth, onAuthStateChanged } from '@angular/fire/auth';
import { GoogleAuthProvider, signInWithPopup, browserLocalPersistence } from "firebase/auth";
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  signedIn$ = new BehaviorSubject<Boolean>(false);

  constructor(private auth: Auth) {

    this.auth.setPersistence(browserLocalPersistence);
    onAuthStateChanged(auth, (user) => {
      if (user) {
        this.signedIn$.next(true);
      } else {
        this.signedIn$.next(false);
      }
    });
  }

  loginWithGoogle() {
    const provider = new GoogleAuthProvider();
    signInWithPopup(this.auth, provider)
      .then((result) => {
        console.log(result);
      }).catch((error) => {
        console.log(error);
      });
  }

  logout() {
    this.auth.signOut();
  }

}

Then open login.page.ts and the code should look like this, again keeping it at minimum code

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

@Component({
  selector: 'app-login',
  templateUrl: './login.page.html',
  styleUrls: ['./login.page.scss'],
})
export class LoginPage implements OnInit {
  signedIn: Boolean = false;

  constructor(private authService: AuthService) {
    this.authService.signedIn$.subscribe((result) => {
      console.log(result);
      this.signedIn = result;
    });
  }

  ngOnInit() {
  }

  loginGoogle() {
    this.authService.loginWithGoogle();
    this.dataService.addNewEvent();
  }
  loginFacebook() {
    this.authService.loginWithFacebook();
  }

  logout() {
    this.authService.logout();
  }
}

The final thing should be in login.page.html it should look something like this

<ion-header>
  <ion-toolbar>
    <ion-title>login</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>

  <div *ngIf="signedIn; else showLogin">
    <ion-button (click)="logout()">
      <ion-icon slot="start" name="log-out-outline"></ion-icon>Logout
    </ion-button>
  </div>
  <ng-template #showLogin>
    <ion-button (click)="loginGoogle()">
      <ion-icon slot="start" name="log-in-outline"></ion-icon>Login with Google
    </ion-button>

  </ng-template>

</ion-content>

I know the observable signedIn$ could be used with async pipe etc.

Test it

If I remembered everything you should be able to test login now. Run the app with

ionic serve

If the app opens without error navigate manually to /login and click on the button to login with google. You should get an option to choose google acount and then you will be logged in and the login button changes to logout.

Emulators

How to connect to the emulators was also changed so here is how I got those to work with version 9 of Firebase.

To connect to the auth emulator you need to do a few more things

First step is to enable/install the emulator, run the command and answer the questions

firebase init emulators

Once installed you can start the emulator(s) with

firebase emulators:start

But the app is not connected to the emulator yet.

In app.component.ts add following

import { Auth, connectAuthEmulator} from '@angular/fire/auth';
import { environment } from '../environments/environment';

....
constructor(private auth: Auth) {
if (environment.useEmulators) {
      connectAuthEmulator(this.auth, 'http://localhost:9099');
    }
}

....

In environment.ts and environment.prod.ts add useEmulators variable and set to true in dev and false in prod.

Now when you start the app it should connect to the emulator instead.

Finished

I hope this helps. I do neither claim this is the best or most correct way. It is what I came up with to avoid using the compat version of AngularFire.

Any comments are welcome. I cannot guarantee I can answer any questions šŸ™‚

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s