angularfire icon indicating copy to clipboard operation
angularfire copied to clipboard

AppCheck Build Error During Prerender for Angular SSR

Open asithade opened this issue 4 years ago • 9 comments

Version info

Angular: 13.0.3

Firebase: 9.6.4

AngularFire: 7.2.0

Other (e.g. Ionic/Cordova, Node, browser, operating system): Node 14

How to reproduce these conditions

Steps to set up and reproduce

After setting up AppCheck as such:


import { initializeAppCheck, provideAppCheck, ReCaptchaV3Provider } from '@angular/fire/app-check';

    provideAppCheck(() => initializeAppCheck(getApp(), { provider: new ReCaptchaV3Provider(environment.recaptchaSiteKey), isTokenAutoRefreshEnabled: true })),

I am able to serve my application with both yarn server and yarn dev:ssr and I'm able to load the site with AppCheck seemingly working. However, when I try to run yarn prerender, I get the following error:

Unhandled Promise rejection: document is not defined ; Zone: <root> ; Task: Promise.then ; Value: ReferenceError: document is not defined

asithade avatar Jan 21 '22 05:01 asithade

This issue does not seem to follow the issue template. Make sure you provide all the required information.

google-oss-bot avatar Jan 21 '22 05:01 google-oss-bot

Did you find a solution for this?

ribalnasr avatar Feb 21 '22 13:02 ribalnasr

Here's my workaround for now...

I created a service:

@Injectable({
    providedIn: 'root'
})
export class RecaptchaBrowser {

    constructor(
        @Inject(PLATFORM_ID) private platformId: string
    ) { }

    provider(siteKey: string) {
        return isPlatformBrowser(this.platformId)
            ? new ReCaptchaV3Provider(siteKey)
            : new CustomProvider({
                getToken: () => new Promise(() => { })
            })
    }
}

and then I import the AppCheck module as follows:

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

This will skip the ReCaptchaV3Provider for server-side rendering and import it only in the browser.

ribalnasr avatar Feb 21 '22 15:02 ribalnasr

I have the same issue. Is AppCheck not supported in Angular Universal?

miikaeru avatar Mar 19 '22 01:03 miikaeru

There is actually an example at https://github.com/angular/angularfire/blob/master/samples/advanced/src/app/app.module.ts#L47-L57

You need to use the admin sdk on server

posva avatar Nov 30 '22 15:11 posva

Has anyone found a solution for this? The closest I've gotten is to check if the app is being checked on the server; if it is, then I create a custom provider similar to @ribalnasr. The issue with this is that it only works if app check is not enforced on Cloud Firestore. Once it is actually enforced, the request from the server will be rejected, which is to be expected. I haven't found a way to generate valid recaptchav3 token on the sever. If there's a way to create this token then we should be able to use it in a custom provider for app check.

MawRojas avatar Jan 25 '23 18:01 MawRojas

I have the same issue: Angular SSR v17

Abdulali97 avatar Dec 19 '23 12:12 Abdulali97

this works for me

app config

const FIREBASE_ADMIN = new InjectionToken<app.App>('firebase-admin');

export function initializeAppCheckFactory(
  platformId: Object,
  injector: Injector
) {
  if (isPlatformBrowser(platformId)) {
    const admin = injector.get<app.App | null>(FIREBASE_ADMIN, null);
    if (admin) {
      const provider = new CustomProvider({
        getToken: () =>
          admin
            .appCheck()
            .createToken(environment.firebase.appId, { ttlMillis: 604_800_000 })
            .then(({ token, ttlMillis: expireTimeMillis }) => ({
              token,
              expireTimeMillis,
            })),
      });
      return initializeAppCheck(undefined, {
        provider,
        isTokenAutoRefreshEnabled: false,
      });
    } else {
      const provider = new ReCaptchaEnterpriseProvider(
        environment.recaptchaEnterpriseSiteKey
      );
      return initializeAppCheck(undefined, {
        provider,
        isTokenAutoRefreshEnabled: true,
      });
    }
  }
  return undefined;
}

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: AppCheck,
      useFactory: (platformId: Object, injector: Injector) =>
        initializeAppCheckFactory(platformId, injector),
      deps: [PLATFORM_ID, Injector],
    },
    {
      provide: FIREBASE_OPTIONS,
      useValue: environment.firebase,
    },
    provideClientHydration(),
    provideAnimationsAsync(),
    provideRouter(
      appRoutes,
      withEnabledBlockingInitialNavigation(),
      withViewTransitions()
      // withDebugTracing()
    ),
    importProvidersFrom(
      provideFirebaseApp(() => initializeApp(environment.firebase)),
      provideAuth(() => getAuth())
    ),
    provideStore({ auth: authReducer }),
    provideEffects([AuthEffects]),
  ],
};

and Injecting AppCheck into AppComponent ensures initialization in a client-side context

export class AppComponent{
  constructor(
    private router: Router,
    private authFacade: AuthFacade,
    private appCheck: AppCheck,
  ) {} 

Abdulali97 avatar Dec 19 '23 15:12 Abdulali97

https://github.com/angular/angularfire/blob/master/samples/advanced/src/app/app.module.ts#L47-L57

It seems like a good solution but it has a dependency on the "firebase-admin" package. In my case, I just want to configure the app-check correctly but I don't need the firebase-admin.

It would be great if angular-fire provided a correct example of recommended implementation for AppCheck with SSR and prerender

hittten avatar Dec 23 '23 16:12 hittten