From 62c3aab64739cbe9dbce74f094f3d501e97b0f3c Mon Sep 17 00:00:00 2001 From: Armando Navarro Date: Sun, 27 Sep 2026 20:56:55 -0700 Subject: [PATCH] docs(app-check): explain App Check during server rendering The App Check guide said nothing about server-rendered apps. #3773 stops AngularFire from creating an App Check instance during server rendering, so the new section explains that inject(AppCheck) is null there, that Firebase requests made while rendering carry no App Check token, and how to opt back in with APP_CHECK_ON_SERVER. It also shows a browser-only config for 21.0.0-rc.1 and earlier, where the guide's setup fails with "document is not defined". --- docs/app-check.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/docs/app-check.md b/docs/app-check.md index 0490b718b..7c2e700fd 100644 --- a/docs/app-check.md +++ b/docs/app-check.md @@ -46,6 +46,73 @@ export class AppCheckComponent { } ``` +## Server-side rendering + +App Check's reCAPTCHA providers need a browser. So while a page renders on the server, AngularFire does not call the function you pass to `provideAppCheck`, and no App Check instance is created there. On the server, `inject(AppCheck)` returns `null`, although its type says `AppCheck`. Code that runs on both the server and the browser should check the platform before using it. + +### Firebase requests made during server rendering + +With no App Check instance on the server, requests to Firebase made while the server renders a page carry no App Check token. If you [enforce App Check](https://firebase.google.com/docs/app-check/enable-enforcement) for a Firebase product, those requests are rejected. Firebase's recommended fix is to hand a token from the browser to the server and pass it to `initializeServerApp` as `appCheckToken`, described in [Use App Check in SSR environments](https://firebase.google.com/docs/web/ssr-apps#use-app-check). + +### Running App Check during server rendering + +If your app runs App Check on the server on purpose, with a provider that works outside the browser such as a `CustomProvider` backed by the Firebase Admin SDK, provide `APP_CHECK_ON_SERVER` in the server-only config: + +```ts +// app.config.server.ts +import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; +import { provideServerRendering, withRoutes } from '@angular/ssr'; +import { APP_CHECK_ON_SERVER } from '@angular/fire/app-check'; +import { appConfig } from './app.config'; +import { serverRoutes } from './app.routes.server'; + +const serverConfig: ApplicationConfig = { + providers: [ + provideServerRendering(withRoutes(serverRoutes)), + { provide: APP_CHECK_ON_SERVER, useValue: true }, + ] +}; + +export const config = mergeApplicationConfig(appConfig, serverConfig); +``` + +The function passed to `provideAppCheck` then runs on the server too, so it has to choose a provider that works there. + +### AngularFire 21.0.0-rc.1 and earlier + +These releases run App Check during server rendering, so the setup in [Dependency Injection](#dependency-injection) fails with `ReferenceError: document is not defined`. Keep `provideAppCheck` out of the config the server uses by moving it into a config only the browser loads: + +```ts +// app.config.browser.ts +import { ApplicationConfig, inject, mergeApplicationConfig } from '@angular/core'; +import { FirebaseApp } from '@angular/fire/app'; +import { provideAppCheck, initializeAppCheck, ReCaptchaV3Provider } from '@angular/fire/app-check'; +import { appConfig } from './app.config'; + +const browserConfig: ApplicationConfig = { + providers: [ + provideAppCheck(() => initializeAppCheck(inject(FirebaseApp), { + provider: new ReCaptchaV3Provider(/* configuration */), + })), + ] +}; + +export const config = mergeApplicationConfig(appConfig, browserConfig); +``` + +Then bootstrap the browser with that config in `main.ts`: + +```ts +import { bootstrapApplication } from '@angular/platform-browser'; +import { config } from './app/app.config.browser'; +import { App } from './app/app'; + +bootstrapApplication(App, config) + .catch((err) => console.error(err)); +``` + +The server then has no `AppCheck` provider at all, so inject it with `inject(AppCheck, { optional: true })`, which returns `null` on the server as later releases do. + ## Firebase API AngularFire wraps the Firebase JS SDK to ensure proper functionality in Angular, while providing the same API.