Search code examples
angulartypescriptfirebaseangularfire2

Angular/Firebase - Guard runs before Firebase initialization is done


See issue here (open console):

https://stackblitz.com/edit/angular-su5p89?file=src%2Fapp%2Fapp.module.ts

I've imported and called initalizeApp in my AppModule like this:

import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFirestoreModule } from '@angular/fire/firestore';

@NgModule({
  imports: [
    BrowserModule.withServerTransition({ appId: 'serverApp' }),

    // Firebase
    AngularFireModule.initializeApp(environment.firebase),
    AngularFirestoreModule,
    AngularFireAuthModule,

    // App
    AppRoutingModule
  ],
  declarations: [
    AppComponent
  ],
  bootstrap: [AppComponent]
})

export class AppModule {}

Then I created a guard that looks like this:

import { Injectable } from '@angular/core';
import { CanActivate, CanActivateChild, CanLoad } from '@angular/router';
import * as firebase from 'firebase/app';

@Injectable({
  providedIn: 'root'
})

export class AuthGuard implements CanActivate, CanActivateChild, CanLoad {

  /** @ignore */
  constructor() {}

  canLoad(): boolean {
    return this.performCheck();
  }

  canActivate(): boolean {
    return this.performCheck();
  }

  canActivateChild(): boolean {
    return this.performCheck();
  }

  private performCheck() {
    return !!firebase.auth().currentUser;
  }
}

And use it like:

export const Routes = [
  {
    path: '',
    pathMatch: 'full',
    redirectTo: 'hello'
  },
  {
    path: 'hello',
    component: HelloComponent,
    canActivate: [AuthGuard]
  }
];

However, this yields the following error:

ERROR Error: Uncaught (in promise): [DEFAULT]: Firebase: No Firebase App '[DEFAULT]' has been created - call Firebase App.initializeApp() (app/no-app). [DEFAULT]: Firebase: No Firebase App '[DEFAULT]' has been created - call Firebase App.initializeApp() (app/no-app). at error (index.cjs.js:361) at app (index.cjs.js:244) at Object.serviceNamespace [as auth] (index.cjs.js:302) at AuthGuard.performCheck (auth.guard.ts:35) at AuthGuard.canActivate (auth.guard.ts:29) at eval (check_guards.ts:121) at Observable.eval [as _subscribe] (defer.ts:59) at Observable._trySubscribe (Observable.ts:231) at Observable.subscribe (Observable.ts:212) at TakeOperator.call (take.ts:72) at resolvePromise (zone.js:831) at resolvePromise (zone.js:788) at eval (zone.js:892) at ZoneDelegate.invokeTask (zone.js:423) at Object.onInvokeTask (ng_zone.ts:262) at ZoneDelegate.invokeTask (zone.js:422) at Zone.runTask (zone.js:195) at drainMicroTaskQueue (zone.js:601)

Which when looking at other issues means the app haven't initialized Firebase yet, which is odd since I do that before anything else.. And in theory the guard should run AFTER all of this happens, right?

How can I solve this?


Solution

  • I would configure the guard differently because for some reason this line:

    import * as firebase from 'firebase/app';

    is causing a second initialisation and there is no initializeApp() method called which causes the error.

    import { Injectable } from '@angular/core';
    import { CanActivate, CanActivateChild, CanLoad } from '@angular/router';
    import { AngularFireAuth } from '@angular/fire/auth'; // instead of firebase
    import { Observable } from 'rxjs';
    import { take, map } from 'rxjs/operators'
    
    @Injectable({
      providedIn: 'root'
    })
    
    export class AuthGuard implements CanActivate, CanActivateChild, CanLoad {
    
      /** @ignore */
      constructor(private auth: AngularFireAuth) {}
    
      canLoad(): Observable<boolean> {
        return this.performCheck();
      }
    
      canActivate(): Observable<boolean> {
        return this.performCheck();
      }
    
      canActivateChild(): Observable<boolean> {
        return this.performCheck();
      }
    
      private performCheck(): Observable<boolean> {
        return this.auth.user.pipe(map((user) => {
          if (user) {
            // user is logged in
            return true;
          }
          // user is not logged in
          return false;
        }), 
        take(1));
      }
    }
    

    updated stackBlitz