Firebase Google sign-in in Angular 20+ with AngularFire
Install the AngularFire major that matches your Angular major (on Angular 22, which has no stable AngularFire yet, use the plain Firebase JS SDK), register provideFirebaseApp() and provideAuth() in app.config.ts, then call signInWithPopup() from a click handler. Keep the default authDomain for local development; proxy /__/auth/* to Firebase in production only if you need redirect sign-in or want Google to show your own domain.
Pick the right AngularFire version first
AngularFire majors track Angular majors, and each release declares the Angular range it supports as a peer dependency, so a mismatched pair fails at install time. Checked against the npm registry as of Oct 2026:
| Your Angular version | Install | Bundled Firebase JS SDK |
|---|---|---|
| 20.x | npm install @angular/fire@20 |
firebase ^11.8.0 |
| 21.2 or later 21.x | npm install @angular/fire@21 |
firebase ^12.18.0 |
| 22.x | No stable AngularFire release yet (only 22.0.0-canary builds). Use the plain Firebase JS SDK (npm install firebase) or wait for v22. |
— |
Check your Angular version with ng version. As of Oct 2026 the latest Angular on npm is 22.x, so a project you create today with ng new is likely on Angular 22. AngularFire 21.0.0 was published on Oct 26, 2026 and declares @angular/core ^21.2.0; recheck the npm registry if you read this later. AngularFire also offers ng add @angular/fire, which installs and wires the package for you. Let AngularFire bring its own firebase dependency rather than installing a different firebase major next to it, and import auth functions from @angular/fire/auth, not firebase/auth, so AngularFire can wrap them.
On Angular 22: the plain Firebase JS SDK
Steps 2 and 3 use AngularFire. Without it, create the app and auth instance once in a plain module and use the modular SDK directly:
// src/app/firebase.ts
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { environment } from '../environments/environment';
export const auth = getAuth(initializeApp(environment.firebase));
In your component, import GoogleAuthProvider and signInWithPopup from firebase/auth and auth from ./firebase, call signInWithPopup(auth, new GoogleAuthProvider()) inside the click handler, and track the user with onAuthStateChanged(auth, ...) written into a signal. You lose AngularFire's zone and SSR wrapping, so with SSR keep these calls in the browser (see Step 4).
Step 1: Enable Google in Firebase and authorize your domains
- In the Firebase console, open Security > Authentication (older guides show Build > Authentication; as of Oct 2026 the Firebase docs use Security). On the Sign-in method tab, enable Google and save.
- On the Settings tab, under Authorized domains, add every host that serves your app:
localhostforng serve, plus your production domain. Projects created after April 28, 2025 no longer includelocalhostby default.
More detail on that list: Firebase authorized domains.
Step 2: Provide Firebase and Auth (standalone APIs)
Put your web config in an environment file. Run ng generate environments once if you don't have src/environments/ yet; the CLI swaps environment.ts for environment.development.ts during ng serve.
// src/environments/environment.ts (production)
export const environment = {
firebase: {
apiKey: 'YOUR_API_KEY',
authDomain: 'YOUR-PROJECT.firebaseapp.com',
projectId: 'YOUR-PROJECT',
appId: 'YOUR_APP_ID',
},
};
// src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
import { provideAuth, getAuth } from '@angular/fire/auth';
import { routes } from './app.routes';
import { environment } from '../environments/environment';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAuth(() => getAuth()),
],
};
Step 3: Add the Google sign-in button
// src/app/login.component.ts
import { Component, inject, signal } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { Auth, GoogleAuthProvider, signInWithPopup, signOut, user } from '@angular/fire/auth';
@Component({
selector: 'app-login',
imports: [AsyncPipe],
template: `
@if (user$ | async; as u) {
<p>Signed in as {{ u.email }}</p>
<button (click)="logout()">Sign out</button>
} @else {
<button (click)="login()">Sign in with Google</button>
}
@if (error()) {
<p role="alert">{{ error() }}</p>
}
`,
})
export class LoginComponent {
private auth = inject(Auth);
readonly user$ = user(this.auth); // emits null when signed out
readonly error = signal('');
async login() {
this.error.set('');
try {
await signInWithPopup(this.auth, new GoogleAuthProvider());
} catch (e: any) {
this.error.set(e?.code ?? 'Sign-in failed');
}
}
logout() {
return signOut(this.auth);
}
}
The popup is started directly from the click handler, so it only ever runs in the browser, and MDN notes that a popup gated on user activation "must be triggered from a UI element's event handler." The error goes into a signal so the template updates in zoneless apps too.
Step 4: SSR caveats (ng new --ssr projects)
- Prerendered routes render signed out.
ng new --ssrsets routes toRenderMode.Prerender, so the HTML is built with no user. For account pages, either render them in the browser or follow AngularFire's server-render guide (ID token in a__sessioncookie plusinitializeServerApp).
```ts // src/app/app.routes.server.ts import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [ { path: 'account', renderMode: RenderMode.Client }, { path: '**', renderMode: RenderMode.Prerender }, ]; ```
- Keep browser-only work out of the server.
window,documentandlocationdon't exist during server rendering. If you callgetRedirectResult()on page load, run it insideafterNextRender(() => { ... })so it is skipped on the server. - Expired tokens. AngularFire's docs note the server can't refresh an expired ID token, so the page renders signed out until the browser refreshes it.
Step 5 (optional): proxy /__/auth/* so sign-in runs on your own domain
You need this if you use signInWithRedirect() (Firebase's redirect best practices require a setup change for Chrome M115+, Firefox 109+ and Safari 16.1+ when the app isn't on firebaseapp.com), or if you want Google's account chooser to show your domain instead of YOUR-PROJECT.firebaseapp.com.
Local development: keep the default. Leave authDomain as YOUR-PROJECT.firebaseapp.com in environment.development.ts and use signInWithPopup() on ng serve. The Firebase JS SDK always loads its helper pages from https://<authDomain>/__/auth/..., so pointing authDomain at localhost:4200 does not work with a plain-HTTP ng serve. For redirect sign-in on localhost, see signInWithRedirect on localhost.
Production. Firebase's redirect best practices describe a reverse proxy that forwards GET and POST requests for /__/auth/ to YOUR-PROJECT.firebaseapp.com, and say the forwarding must be transparent; a 302 redirect does not work. Whether you also need /__/firebase/init.json is covered in do I need init.json?.
- Firebase Hosting: nothing to proxy. Hosting reserves
/__/and serves the auth helpers itself; setauthDomainto your Hosting custom domain. - Angular SSR on Express (
server.ts): add this before the static and Angular handlers.
```ts import { createProxyMiddleware } from 'http-proxy-middleware';
app.use( createProxyMiddleware({ target: 'https://YOUR-PROJECT.firebaseapp.com', changeOrigin: true, pathFilter: '/__/auth', }), ); ```
Test this on a preview deployment before relying on it.
- nginx in front of your build (Firebase's docs show the same
locationblock):
nginx
location /__/auth {
proxy_pass https://YOUR-PROJECT.firebaseapp.com;
proxy_ssl_server_name on; # send the upstream host name during the TLS handshake
}
Test this on a preview deployment before relying on it.
Then, for every domain that now serves /__/auth:
- Set
authDomaininenvironment.tsto that domain (for exampleapp.example.com). Change nothing else in the config. - In Google Cloud, open the Clients page (
console.cloud.google.com/auth/clients, as of Oct 2026), edit the Web client Firebase created, and addhttps://app.example.com/__/auth/handlerto Authorized redirect URIs. Google requires HTTPS redirect URIs for anything other than localhost. - Keep the domain in Firebase's authorized domains list.
Full walkthrough of the authDomain switch: change Firebase authDomain.
How to confirm it worked
- Click Sign in with Google. The popup should list your Google accounts and close after you pick one, and the template should show "Signed in as ...".
- Reload the page.
user$should emit the same user again without another sign-in. - If you switched
authDomain, open DevTools > Network during sign-in: requests to/__/auth/handlershould go to your own domain and return 200, and the Google screen should name your domain.
Related problems
auth/unauthorized-domainright after deploy: the production host is missing from Authorized domains.Error 400: redirect_uri_mismatchafter changingauthDomain: the/__/auth/handlerURI isn't on the OAuth client. See redirect_uri_mismatch.- Redirect sign-in returns to the page with no user on
ng serve: see signInWithRedirect on localhost.
FAQ
Which AngularFire version works with Angular 20?
@angular/fire 20.x, which declares @angular/core ^20.0.0 as its peer. AngularFire 21 requires Angular 21.2 or later, and as of Oct 2026 there is no stable AngularFire for Angular 22.
Why do I see "Calling Firebase APIs outside of an Injection context"?
AngularFire logs this dev-mode warning when a wrapped API runs outside an injection context, such as inside an async callback. Its docs say signing a user in from user input is one of the cases where this is usually harmless; for long-lived reads, capture an EnvironmentInjector and use runInInjectionContext.
Can I use signInWithRedirect instead of a popup in Angular?
Yes, but unless your app is served from firebaseapp.com or Firebase Hosting, Firebase's redirect best practices require you to proxy /__/auth/* (Step 5) or self-host the helper files. Call getRedirectResult() in the browser only, inside afterNextRender.
Can I set authDomain to localhost:4200 with a proxy.conf.json?
Not with a plain ng serve. The Firebase JS SDK builds its helper URLs as https://<authDomain>/__/auth/..., and ng serve serves HTTP by default, so keep the default firebaseapp.com authDomain locally. The production forward has to come from your server or host (Express, nginx) or from Firebase Hosting.
Is it OK to commit the Firebase config in environment.ts?
Firebase states that API keys for Firebase services are OK to include in code. Restrict the key in Google Cloud and protect data with Security Rules; never put real server secrets in Angular environment files.
Sources
- @angular/fire package metadata (npm registry)
- AngularFire repository README
- AngularFire: Authentication
- AngularFire: Zone Wrappers
- Firebase: Authenticate Using Google with JavaScript
- Firebase: Best practices for using signInWithRedirect on browsers that block third-party storage access
- Firebase: Authenticate with Firebase Using Email Link in JavaScript (authorized domains path, localhost default)
- Firebase Hosting: Reserved URLs
- Firebase: Learn about using and managing API keys
- Firebase JS SDK source: packages/auth/src/core/util/handler.ts (helper URL built as https://authDomain)
- @angular/core package metadata (npm registry)
- Angular: Configuring application environments
- Angular: Server-side and hybrid rendering
- MDN: Transient activation
- http-proxy-middleware README
- Google Identity: Using OAuth 2.0 for Web Server Applications (redirect URI rules)
How this page was made: drafted with AI from the sources listed, reviewed by Pewterkit. Docs checked 26 Oct 2026. Spotted something out of date? Tell us.