Search code examples
angularfirebase-authenticationangular-standalone-components

Firebase authentication setup in standalone Angular


Im using firebase's storage for one of my projects that has an Angular 17 standalone frontend. I wanted to integrate firebase authentication as well. Since I use use standalone angular, I dont have ngmodules. Here is the setup of the firebaseapp, storage and getAuth in my app.config.ts:

import { ApplicationConfig, importProvidersFrom } from '@angular/core';
import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
import { provideStorage, getStorage } from '@angular/fire/storage';
import { provideAuth, getAuth } from '@angular/fire/auth';
//bunch of other irrelevant imports

import { environment } from 'myenvpath';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideAnimationsAsync(),
    importProvidersFrom([BrowserAnimationsModule], MatDialogModule),
    provideHttpClient(withInterceptors([authInterceptor])),
    JwtstoreService,
    {
      provide: JWT_OPTIONS,
      //jwt setup
    },
    JwtHelperService,
    provideFirebaseApp(() => initializeApp(environment.firebaseConfig)),
    provideStorage(() => getStorage()),
    provideAuth(() => getAuth()),
  ],
};

I use custom jwt token for the firebase authentication which i have in localstorage. I made this storage.service.ts to handle the authentication. It also has one method to get data from the firebase storage.

import { Injectable } from '@angular/core';
import { getStorage, ref, getDownloadURL } from '@angular/fire/storage';
import { getAuth, signInWithCustomToken } from '@angular/fire/auth';

@Injectable({
  providedIn: 'root',
})
export class StorageService {
  private authInitialized = false;

  constructor() {}

  private async initializeAuth(): Promise<void> {
    if (this.authInitialized) return;

    const firebaseToken = localStorage.getItem('fbtoken');
    if (!firebaseToken) {
      throw new Error('Firebase token is missing.');
    }

    const auth = getAuth();
    await signInWithCustomToken(auth, firebaseToken);
    this.authInitialized = true;
  }

  async getDownloadURL(path: string): Promise<string> {
    await this.initializeAuth();
    const storage = getStorage();
    const storageRef = ref(storage, path);
    return await getDownloadURL(storageRef);
  }
}

In my components, I simply call the getDownloadURL() method if I want to get something from the storage (of course the storage service is injected in the constructor in the component)

When I launch my app and get to a point where an interaction with the storage is initiated (getDownloadURL() method is called), Im getting the following error in the browser console:

Error: Either AngularFireModule has not been provided in your AppModule (this can be done manually or implictly using
provideFirebaseApp) or you're calling an AngularFire method outside of an NgModule (which is not supported).

Since I dont get a "token missing" or other errors like this, it seems the problem is the the app.config and/or the storage.service setup. The error keeps mentioning appmodule, but again I use standalone angular, dont have an app.module or @ngmodules...

I dont think the error is coming from my backend either as Im getting my custom token back just fine.

For a while I used the firebase storage without the authentication integration and it worked fine.

Any help is appreciated!


Solution

  • I dont understand why, but this worked:

    import { Injectable, inject } from '@angular/core';
    import { getStorage, ref, getDownloadURL } from '@angular/fire/storage';
    import { Auth, getAuth, signInWithCustomToken } from '@angular/fire/auth';
    
    @Injectable({
      providedIn: 'root',
    })
    export class StorageService {
      private authInitialized = false;
      private auth = inject(Auth); //Auth injection
    
      constructor() {}
    
      private async initializeAuth(): Promise<void> {
        if (this.authInitialized) return;
    
        const firebaseToken = localStorage.getItem('fbtoken');
        if (!firebaseToken) {
          throw new Error('Firebase token is missing.');
        }
        this.auth = getAuth(); //refer getAuth() to this.auth
        await signInWithCustomToken(this.auth, firebaseToken);
        this.authInitialized = true;
      }
    
      async getDownloadURL(path: string): Promise<string> {
        await this.initializeAuth();
        const storage = getStorage();
        const storageRef = ref(storage, path);
        return await getDownloadURL(storageRef);
      }
    }
    

    It seems I needed to inject Auth this way...