Introduction
AngularFire Auth connects Angular applications with the Firebase Authentication service. It provides reactive wrappers around user authentication state changes, ID token refreshes, and multi-provider sign-ins, allowing you to secure routes and bind user profiles directly to Angular Signals.
Key Capabilities:
- Reactive
user()andauthState()observables easily convertible to Signals viatoSignal()- Email/password, passwordless email links, and OAuth providers (Google, GitHub, Apple, Microsoft)
- Functional
canActivateroute guards with built-in redirect pipelines- Automatic token refresh and custom claims support for role-based authorization
Authentication Architecture
AngularFire coordinates authentication state between Firebase's client SDK, browser persistence (indexedDB/localStorage), and Angular's reactivity graph:
┌────────────────────────────────────────────────────────┐
│ Firebase Auth SDK │
├──────────────────────────┬─────────────────────────────┤
│ Credentials / OAuth │ Google, GitHub, Apple, PW │
├──────────────────────────┼─────────────────────────────┤
│ State Dispatcher │ authState() / user() │
├──────────────────────────┼─────────────────────────────┤
│ AngularFire Bridge │ toSignal(authState(auth)) │
├──────────────────────────┼─────────────────────────────┤
│ Application Layer │ Signals, Guards, Templates │
└──────────────────────────┴─────────────────────────────┘
Setup & Configuration
To enable Authentication in your application, add provideAuth() to your ApplicationConfig:
import { ApplicationConfig } from '@angular/core';
import { initializeApp, provideFirebaseApp } from '@angular/fire/app';
import { getAuth, provideAuth } from '@angular/fire/auth';
export const appConfig: ApplicationConfig = {
providers: [
provideFirebaseApp(() => initializeApp({ /* firebaseConfig */ })),
provideAuth(() => getAuth()),
],
};
Supported Identity Providers
AngularFire supports all standard Firebase Authentication providers:
| Provider | Method | Best For |
|---|---|---|
GoogleAuthProvider |
One-click login on Android, Chrome, and Web | |
| Email & Password | signInWithEmailAndPassword |
Traditional credential-based user accounts |
| Email Link | sendSignInLinkToEmail |
Passwordless sign-in with magic links |
| GitHub / Gitlab | GithubAuthProvider |
Developer-focused platforms and tools |
| Apple | OAuthProvider('apple.com') |
Required for iOS App Store compliance |
| Anonymous | signInAnonymously |
Guest checkouts and frictionless onboarding |
Signal-Based Auth Service
Creating a centralized authentication service using Angular Signals gives your entire application instant, reactive access to the current user state without subscribing in templates.
import { Injectable, computed, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import {
Auth,
User,
authState,
signInWithPopup,
GoogleAuthProvider,
signOut,
signInWithEmailAndPassword,
createUserWithEmailAndPassword,
} from '@angular/fire/auth';
@Injectable({
providedIn: 'root',
})
export class AuthService {
private auth = inject(Auth);
// Core reactive signal: holds current User or null
readonly currentUser = toSignal<User | null>(authState(this.auth), {
initialValue: null,
});
// Computed signals for common UI states
readonly isLoggedIn = computed(() => this.currentUser() !== null);
readonly userEmail = computed(() => this.currentUser()?.email ?? '');
readonly displayName = computed(() => this.currentUser()?.displayName ?? 'Anonymous');
readonly photoUrl = computed(() => this.currentUser()?.photoURL ?? '');
// Sign in with Google Popup
async loginWithGoogle(): Promise<User> {
const provider = new GoogleAuthProvider();
provider.addScope('profile');
provider.addScope('email');
const credential = await signInWithPopup(this.auth, provider);
return credential.user;
}
// Sign in with Email and Password
async loginWithEmail(email: string, pass: string): Promise<User> {
const credential = await signInWithEmailAndPassword(this.auth, email, pass);
return credential.user;
}
// Register with Email and Password
async registerWithEmail(email: string, pass: string): Promise<User> {
const credential = await createUserWithEmailAndPassword(this.auth, email, pass);
return credential.user;
}
// Sign out
async logout(): Promise<void> {
await signOut(this.auth);
}
}
Consuming in Components
With the AuthService implemented, your component templates can leverage the new control flow syntax (@if, @else) directly with Angular Signals:
import { Component, inject } from '@angular/core';
import { AuthService } from './auth.service';
@Component({
selector: 'app-user-profile',
template: `
<div class="profile-card">
@if (auth.isLoggedIn()) {
<div class="user-info">
@if (auth.photoUrl()) {
<img [src]="auth.photoUrl()" alt="Avatar" class="avatar" />
}
<h3>{{ auth.displayName() }}</h3>
<p>{{ auth.userEmail() }}</p>
<button (click)="auth.logout()">Sign Out</button>
</div>
} @else {
<div class="sign-in-prompt">
<p>Please sign in to access your dashboard.</p>
<button (click)="auth.loginWithGoogle()">Sign in with Google</button>
</div>
}
</div>
`,
})
export class UserProfileComponent {
readonly auth = inject(AuthService);
}
Route Protection & Functional Guards
Protect your routes using modern Angular functional router guards combined with AngularFire's AuthPipe generators.
Method 1: Using AngularFire AuthGuard
AngularFire provides an out-of-the-box AuthGuard along with helper pipes for redirection:
import { Routes } from '@angular/router';
import { AuthGuard, redirectUnauthorizedTo, redirectLoggedInTo } from '@angular/fire/auth-guard';
const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['/login']);
const redirectLoggedInToDashboard = () => redirectLoggedInTo(['/dashboard']);
export const routes: Routes = [
{
path: 'login',
loadComponent: () => import('./login.component'),
canActivate: [AuthGuard],
data: { authGuardPipe: redirectLoggedInToDashboard },
},
{
path: 'dashboard',
loadComponent: () => import('./dashboard.component'),
canActivate: [AuthGuard],
data: { authGuardPipe: redirectUnauthorizedToLogin },
},
];
Method 2: Custom Functional Guard with Signals
You can also construct a lightweight functional guard utilizing inject(Auth) and inject(Router):
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { Auth, authState } from '@angular/fire/auth';
import { map, take } from 'rxjs/operators';
export const authGuard: CanActivateFn = () => {
const auth = inject(Auth);
const router = inject(Router);
return authState(auth).pipe(
take(1),
map((user) => {
if (user) {
return true;
}
return router.createUrlTree(['/login']);
})
);
};
Role-Based Authorization & Custom Claims
To restrict content based on user roles (e.g. admin, editor), read Firebase Custom Claims using idTokenResult:
import { inject, Injectable } from '@angular/core';
import { Auth, idToken } from '@angular/fire/auth';
import { toSignal } from '@angular/core/rxjs-interop';
import { map } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class RoleService {
private auth = inject(Auth);
readonly claims = toSignal(
idToken(this.auth).pipe(
map(async (token) => {
if (!token || !this.auth.currentUser) return null;
const result = await this.auth.currentUser.getIdTokenResult();
return result.claims;
})
)
);
}
SSR & Hydration Considerations
When rendering pages on the server with AnalogJS or Angular SSR:
- Client-only Auth Operations: Authentication state typically initializes on the browser client. Use
isPlatformBrowser(this.platformId)when calling methods likesignInWithPopupor checking browser cookies. - Session Cookies for SSR: For full SSR authentication where the server renders personalized content, pass Firebase session cookies in incoming requests and verify them using the Firebase Admin SDK within an Analog server route (
src/server/routes/api/auth.ts).