Modular Auth with compat AuthGuards
  • 24-Apr-2023
Lightrun Team
Author Lightrun Team
Share
Modular Auth with compat AuthGuards

Modular Auth with compat AuthGuards throws R3InjectorError (NullInjectorError: No provider for InjectionToken angularfire2.app.options!)

Lightrun Team
Lightrun Team
24-Apr-2023

Explanation of the problem

The following technical description outlines the steps and conditions required to reproduce an issue related to the usage of modular Auth with compat AuthGuards in an Angular 12.2.7 app with Firebase 9.3.0 and AngularFire 7.0.4. The error occurs on Node 12.18.3 and macOS Big Sur (11.2.3) operating system.

To reproduce the issue, the app needs to be created using the new modular providers as shown in the code block below in the app.module.ts file:

import { provideFirebaseApp, initializeApp, getApp } from '@angular/fire/app';
import {
  initializeAuth,
  provideAuth,
  browserPopupRedirectResolver,
  indexedDBLocalPersistence,
  connectAuthEmulator,
} from '@angular/fire/auth';

@NgModule({
...
imports: [
    provideFirebaseApp(() => initializeApp(environment.firebase)),
    provideAuth(() => {
      const auth = initializeAuth(getApp(), {
        persistence: indexedDBLocalPersistence,
        popupRedirectResolver: browserPopupRedirectResolver,
      });
      if (environment.emulator) {
        connectAuthEmulator(auth, 'http://localhost:9099', {
          disableWarnings: true,
        });
      }
      return auth;
    })
...
],
...

Once the app is created, the next step is to create auth guards, which can be done by importing the required modules and using the canActivate method, as shown in the code block below in the app-routing.module.ts file:

import {
  redirectLoggedInTo,
  canActivate,
} from '@angular/fire/compat/auth-guard';

const redirectToDashboard = () => {
  return redirectLoggedInTo('dashboard');
};

const routes: Routes = [
  {
    path: '',
    component: LandingPageComponent,
    ...canActivate(redirectToDashboard),
    data: { animation: 'Sign in' },
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })],
  exports: [RouterModule],
})
export class AppRoutingModule {}

However, when using the above code in conjunction with modular Auth and compat AuthGuards, a null injector error occurs, as shown in the debug output below:

 

core.js:6479 ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(AppModule)[AngularFireAuthGuard -> AngularFireAuth -> InjectionToken angularfire2.app.options -> InjectionToken angularfire2.app.options -> InjectionToken angularfire2.app.options]:
NullInjectorError: No provider for InjectionToken angularfire2.app.options!
NullInjectorError: R3InjectorError(AppModule)[AngularFireAuthGuard -> AngularFireAuth -> InjectionToken angularfire2.app.options -> InjectionToken angularfire2.app.options -> InjectionToken angularfire2.app.options]:
NullInjectorError: No provider for InjectionToken angularfire2.app.options!
...

This issue can be resolved by either not using AuthGuards in the app or by providing a modular AuthGuards solution along with a sample project.

Troubleshooting with the Lightrun Developer Observability Platform

Getting a sense of what’s actually happening inside a live application is a frustrating experience, one that relies mostly on querying and observing whatever logs were written during development.
Lightrun is a Developer Observability Platform, allowing developers to add telemetry to live applications in real-time, on-demand, and right from the IDE.

  • Instantly add logs to, set metrics in, and take snapshots of live applications
  • Insights delivered straight to your IDE or CLI
  • Works where you do: dev, QA, staging, CI/CD, and production

Start for free today

Problem solution for error Modular Auth with compat AuthGuards throws R3InjectorError

 

To use AngularFireAuthGuard in Angular 12, you need to import the ‘old’ modules. This means importing AngularFireModule.initializeApp(environment.firebase) and AngularFireAuthModule. This should allow you to use AngularFireAuthGuard without any issues.

If the above solution does not work for you, there is an alternative solution that involves modifying the code slightly. This solution involves using the AngularFireAuthGuard class and implementing the CanActivate interface. Within the class, you can define an observable for the user state using getAuth() and authState() from “@angular/fire/auth”. You can also define an AuthPipeGenerator and AuthPipe to check for user authentication and authorization.

The AuthPipeGenerator is a function that takes in the next ActivatedRouteSnapshot and RouterStateSnapshot and returns an AuthPipe. The AuthPipe is a function that takes in an Observable of type auth.User or null and returns an Observable of type boolean, string, or any[]. You can then use these functions to check for authentication and authorization before allowing access to the desired route.

There are also several predefined AuthPipes available, such as loggedIn, isNotAnonymous, emailVerified, customClaims, hasCustomClaim, redirectUnauthorizedTo, and redirectLoggedInTo. These predefined AuthPipes can simplify the process of checking for authentication and authorization, making it easier to use the AngularFireAuthGuard.

 

Other popular problems when working with AngularFireAuth in Angular

 

Problem: “Firebase is not defined” error.

One of the most common problems that Angular developers face while using AngularFire is the “Firebase is not defined” error. This occurs when the Angular app is not able to find the Firebase library that it needs to connect to Firebase services. The error can occur due to a variety of reasons, such as incorrect configuration, missing dependencies, or issues with the Firebase SDK.

Solution:

To fix this issue, developers should ensure that the Firebase library is properly configured in their Angular app. This includes importing the AngularFire module, initializing it with the Firebase credentials, and ensuring that the Firebase SDK is properly installed as a dependency. Here’s an example of how to import and initialize the AngularFire module:

import { AngularFireModule } from '@angular/fire';
import { environment } from '../environments/environment';

@NgModule({
  imports: [
    AngularFireModule.initializeApp(environment.firebase)
  ],
  ...
})
export class AppModule { }

Problem: “Cannot find name ‘auth'” error

Another common issue with AngularFire is the “Cannot find name ‘auth'” error. This error occurs when the app is not able to find the necessary Firebase authentication objects, such as auth or User.

Solution:

To resolve this issue, developers should ensure that they are importing the correct Firebase authentication objects in their Angular components. This can be done using the following import statements:

import { AngularFireAuth } from '@angular/fire/auth';
import firebase from 'firebase/app';

...
constructor(private afAuth: AngularFireAuth) {}

...

this.afAuth.signInWithPopup(new firebase.auth.GoogleAuthProvider());

Problem: “FirebaseError: Firebase: No Firebase App ‘[DEFAULT]'” error.

This error occurs when an Angular app is not able to establish a connection to the Firebase database. This can happen due to incorrect configuration, network issues, or other problems with the Firebase SDK.

Solution: To fix this issue, developers should ensure that the AngularFire module is initialized with the correct Firebase credentials, and that the Firebase SDK is properly installed and configured as a dependency. Here’s an example of how to initialize the AngularFire module:

import { AngularFireModule } from '@angular/fire';
import { environment } from '../environments/environment';

@NgModule({
  imports: [
    AngularFireModule.initializeApp(environment.firebase)
  ],
  ...
})
export class AppModule { }

A brief introduction to Remix-run React Router

 

AngularFire is a library for building Angular applications that use Firebase services. It provides a set of Angular services and components that simplify the process of interacting with Firebase services, such as the Realtime Database, Cloud Firestore, and Authentication. AngularFire is designed to integrate seamlessly with Angular, providing a set of observables that can be used to manage data and synchronize it with Firebase.

One of the key benefits of AngularFire is that it provides a consistent and easy-to-use API for working with Firebase services. AngularFire abstracts away much of the complexity of working with Firebase, providing a set of high-level services and components that can be used to build complex applications. Additionally, AngularFire integrates seamlessly with Angular’s change detection system, providing a reactive programming model that simplifies the process of managing data and updates. Overall, AngularFire simplifies the process of building complex Angular applications that use Firebase services, providing a set of tools and APIs that make it easier to build scalable and reliable applications.

AngularFire is built on top of Firebase, a cloud-based platform for building mobile and web applications. Firebase provides a set of tools and services for building scalable and reliable applications, including Realtime Database, Cloud Firestore, Authentication, Cloud Functions, and Cloud Storage. Firebase is designed to make it easy to build real-time applications that can scale to millions of users, providing a set of high-level APIs and SDKs that simplify the process of working with complex data and workflows. Overall, Firebase provides a powerful and flexible platform for building modern applications, while AngularFire provides a set of Angular-specific tools and APIs that make it easier to integrate Firebase into Angular applications.

Most popular use cases for Remix-run React Router

 

AngularFire can be used to create real-time, reactive web applications with Angular and Firebase. It provides an Angular-specific wrapper around the Firebase SDK, allowing developers to easily integrate Firebase into their Angular applications without worrying about manual setup and configuration. AngularFire supports real-time data synchronization and authentication, allowing developers to easily build reactive applications that update in real-time as data changes.

One common use case for AngularFire is real-time data synchronization. Using AngularFire, developers can easily bind their Angular templates to Firebase collections, allowing them to automatically update as data changes. For example, the following code block demonstrates how to use AngularFire to bind an Angular template to a Firebase collection:

@Component({
  selector: 'app-users',
  template: `
    <ul>
      <li *ngFor="let user of users | async">{{ user.name }}</li>
    </ul>
  `
})
export class UsersComponent implements OnInit {
  users: Observable<any[]>;

  constructor(private db: AngularFirestore) {}

  ngOnInit() {
    this.users = this.db.collection('users').valueChanges();
  }
}

In this example, AngularFire is used to bind the users Observable to an Angular template. The users Observable is created by calling the valueChanges() method on a Firebase collection reference, which returns a real-time stream of the collection’s documents. As the collection is updated in Firebase, the users Observable will automatically update, causing the Angular template to re-render with the updated data.

Another use case for AngularFire is authentication. AngularFire provides an Angular-specific API for Firebase authentication, allowing developers to easily authenticate users in their Angular applications. For example, the following code block demonstrates how to use AngularFire to authenticate a user with Firebase:

@Component({
  selector: 'app-login',
  template: `
    <form (submit)="login()">
      <input type="email" [(ngModel)]="email" name="email" placeholder="Email">
      <input type="password" [(ngModel)]="password" name="password" placeholder="Password">
      <button type="submit">Login</button>
    </form>
  `
})
export class LoginComponent {
  email: string;
  password: string;

  constructor(private afAuth: AngularFireAuth) {}

  async login() {
    try {
      await this.afAuth.signInWithEmailAndPassword(this.email, this.password);
    } catch (e) {
      console.log(e);
    }
  }
}

In this example, AngularFire is used to authenticate a user with Firebase. The afAuth service is injected into the component’s constructor, allowing the component to access the AngularFire authentication API. When the user submits the login form, the login() method is called, which uses the signInWithEmailAndPassword() method to authenticate the user with Firebase.

Finally, AngularFire can be used to perform serverless functions with Firebase Cloud Functions. AngularFire provides an Angular-specific API for Firebase Cloud Functions, allowing developers to easily trigger and respond to Firebase Cloud Functions in their Angular applications.

Share

It’s Really not that Complicated.

You can actually understand what’s going on inside your live applications.

Try Lightrun’s Playground

Lets Talk!

Looking for more information about Lightrun and debugging?
We’d love to hear from you!
Drop us a line and we’ll get back to you shortly.

By submitting this form, I agree to Lightrun’s Privacy Policy and Terms of Use.