Search code examples
angulartypescriptfirebasefirebase-app-check

How to fix (appCheck/already-initialized) error Angular 17


I created a new angular ssr project and added @angular/fire to it using schematics. The project uses firestore, storage and authentication. I configured the relevant services for app-check and also included my app debug token in the appCheck config in the firebase console.

This is my firebase initialization in my app.config.ts

export const appConfig: ApplicationConfig = {
  providers: [
    importProvidersFrom(
      provideFirebaseApp(() => initializeApp(environment.firebase)),
      provideAppCheck(() =>
        initializeAppCheck(getApp(), {
          provider: new ReCaptchaEnterpriseProvider(environment.siteKey),
          isTokenAutoRefreshEnabled: true,
        })
      )
    ),
  ]
}

When i run npm start for the first time. It displays the following error.

document not defined error image

When I reload the page without any modifications to the code then everything displays fine. But when I make any changes to the code and reload the page, then this happens.

app check error image

That error persists until you kill the terminal and run npm start again and repeat the process above

I've tried checking for existing apps while initializing appCheck as below but it still leads to the same results.

provideAppCheck(() =>
  initializeAppCheck(
    getApps().length === 0 
      ? initializeApp(environment.firebase)
      : getApp(), 
    {
      provider: new ReCaptchaEnterpriseProvider(environment.siteKey),
      isTokenAutoRefreshEnabled: true,
    }
  )
)

Solution

  • This is a problem between @angular/fire/app-check and SSR in Angular 17~18. The error document is not defined appears because the server attempts to initialize AppCheck, but this will only work on the browser, not the server.

    In order to fix this, you have to create a service like this, with a custom provider:

    import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
    import { isPlatformBrowser } from '@angular/common';
    import {
      CustomProvider,
      ReCaptchaEnterpriseProvider,
    } from '@angular/fire/app-check';
    
    @Injectable({
      providedIn: 'root',
    })
    export class RecaptchaBrowser {
      constructor(@Inject(PLATFORM_ID) private platformId: string) {}
    
      provider(siteKey: string) {
        return isPlatformBrowser(this.platformId)
          ? new ReCaptchaEnterpriseProvider(siteKey)
          : new CustomProvider({
              getToken: () => new Promise(() => {}),
            });
      }
    }
    

    And then you initialize the app with your custom provider:

        provideAppCheck((injector) =>
          initializeAppCheck(getApp(), {
            provider: injector
              .get(RecaptchaBrowser)
              .provider(environment.reCaptchaSiteKey),
            isTokenAutoRefreshEnabled: true,
          }),
        ),
    

    And as Naren suggested, you must definitely remove importProvidersFrom( if you're using Angular 17 or 18.