Firebase Authentication offers a dependable, straightforward backend solution for handling user sign-in within your Angular apps.
A wide range of authentication strategies are available, such as email/password, third-party providers like Google and Facebook, as well as phone-based verification.
We'll cover the entire process here: configuring Firebase Authentication, connecting it to your Angular app, and examining different sign-in options, recommended practices, and typical use cases.
Visualize Firebase as the security checkpoint for your application, much like a club's doorman. Gaining access to the premium areas—your app's core data and features—requires valid proof of identity. Configuring Firebase is essentially recruiting that doorman and outlining how they should verify visitor IDs.
Setting Up Firebase in Your Angular Project
A Firebase project is a prerequisite; if you haven't got one yet, head over to the console (console.firebase.google.com) and create it.
Once you're in the console, access your project, hit "Add app," choose "Web," and proceed with the registration steps for your Angular app.
This yields a configuration object armed with API keys and other essential details.Use npm to add the Firebase package to your Angular project's dependencies:
npm install @angular/fire
- Bring in the required Firebase dependencies and set up the Firebase app initialization inside your
app.config.tsfile:
import { provideHttpClient } from '@angular/common/http';
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { initializeApp, provideFirebaseApp } from '@angular/fire/app';
import { getAuth, provideAuth } from '@angular/fire/auth';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideRouter } from '@angular/router';
import { environment } from '../environments/environments';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAuth(() => getAuth()),
provideAnimationsAsync(),
provideHttpClient(),
]
};
Your environment.firebase property — located inside both environment.ts and environment.prod.ts — must hold the complete Firebase configuration object:
export const environment = {
production: false,
firebase: {
apiKey: "-----",
authDomain: "-----",
projectId: "-----",
storageBucket: "-----",
messagingSenderId: "-----",
appId: "-----",
}
};
Email/Password Authentication
Among all authentication options, this one is used most frequently.
- To keep your codebase tidy and support reuse, set up a dedicated service. Start by adding a new file called
auth.service.ts:
import { Injectable } from '@angular/core';
import {
Auth,
browserSessionPersistence,
GoogleAuthProvider,
signInWithEmailAndPassword,
signInWithPopup,
signOut,
user,
User,
} from '@angular/fire/auth';
import { setPersistence } from 'firebase/auth';
import { from, Observable } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class AuthService {
user$: Observable<User | null>;
constructor(private firebaseAuth: Auth) {
this.setSessionStoragePersistence();
this.user$ = user(this.firebaseAuth);
}
private setSessionStoragePersistence(): void {
setPersistence(this.firebaseAuth, browserSessionPersistence);
}
login(email: string, password: string): Observable<void> {
const promise = signInWithEmailAndPassword(
this.firebaseAuth,
email,
password
).then(() => {
//
});
return from(promise);
}
logout(): Observable<void> {
const promise = signOut(this.firebaseAuth).then(() => {
sessionStorage.clear();
});
return from(promise);
}
}
- Bring the
AuthServiceinto your components via dependency injection
import { Component, inject } from '@angular/core';
import {
FormsModule,
Validators,
ReactiveFormsModule,
FormBuilder,
} from '@angular/forms';
import { AuthService } from './service/auth.service';
import { Router, RouterLink } from '@angular/router';
@Component({
selector: 'app-user-login',
standalone:true,
imports: [FormsModule, ReactiveFormsModule, RouterLink],
templateUrl: './login.component.html',
styles: '',
})
export class LoginComponent {
error: boolean = false;
fb: FormBuilder = inject(FormBuilder);
authService: AuthService = inject(AuthService);
router: Router = inject(Router);
form = this.fb.nonNullable.group({
email: [
'',
[
Validators.required,
Validators.pattern(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/),
],
],
password: ['', Validators.required],
});
onSubmit(): void {
const rawForm = this.form.getRawValue();
this.authService.login(rawForm.email, rawForm.password).subscribe({
next: () => {
this.router.navigateByUrl('/protected-content');
},
error: (error) => {
this.error = true;
console.error('Email/Password Sign-In error:', error);
},
});
}
guestLogin(): void {
const values = { email: 'guest@mail.uk', password: 'fake_password' };
this.form.patchValue(values);
const subscription = this.form.valueChanges.subscribe(() => {
if (this.form.valid) {
subscription.unsubscribe();
this.onSubmit();
}
});
}
}
Key Considerations
- Error Handling: It's crucial to implement robust error handling that gives users clear feedback and manages failed authentication attempts gracefully. Firebase exposes detailed error codes, allowing you to tailor your messages accordingly.
- Security Best Practices: Avoid placing sensitive data such as API keys directly in client-side code. Instead, safeguard your credentials through environment variables or server-side logic.
- Observables: Leverage observables to respond to authentication state changes, which lets you adjust your UI dynamically depending on the user's logged-in status.
- Asynchronous Operations: Since Firebase authentication calls are asynchronous, always use async/await or promise-based patterns to process the results.
Social Logins
Working with social login providers becomes much easier with Firebase. Start by activating the providers you need under the Authentication section in your Firebase console. Below is a sample implementation for Google Sign-In:
async onGoogleSignIn(): Promise<void> {
try {
await this.authService.googleLogin();
this.router.navigateByUrl('/main');
} catch (error) {
console.error('Google Sign-In error:', error);
}
}
---
async googleLogin(): Promise<void> {
const provider = new GoogleAuthProvider();
try {
const result = await signInWithPopup(this.firebaseAuth, provider);
const user = result.user;
if (!user) {
throw new Error('Google-Login error');
}
} catch (error) {
console.error('Google-Login error:', error);
throw error;
}
}
Protecting Routes
To secure pages that depend on a logged-in user, rely on Angular's route guard mechanism.
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from '../service/auth.service';
import { inject } from '@angular/core';
import { map } from 'rxjs';
export const authGuard: CanActivateFn = () => {
const authService = inject(AuthService);
const router = inject(Router);
return authService.user$.pipe(
map((user) => {
if (user) {
return true;
} else {
router.navigate(['']);
return false;
}
})
);
};
Next, wire up the guard inside your routing configuration:
import { Routes } from '@angular/router';
import { authGuard } from './guards/auth.guard';
import { LoginComponent } from './login.component';
import { ProtectedContentComponent } from './protected-content.component';
export const routes: Routes = [
{
path: '',
component: LoginComponent,
},
{
path: 'protected-content',
component: ProtectedContentComponent,
canActivate: [authGuard], 👈
},
{ path: '**', redirectTo: '' },
];
Managing User State and Data
Within your components, tap into the user$ observable to track authentication shifts and tailor what the user sees, so their content adapts dynamically:
import { Component, inject, OnInit } from '@angular/core';
import { AuthService } from './service/auth.service';
import { AsyncPipe, CommonModule } from '@angular/common';
import { Router, RouterLink } from '@angular/router';
@Component({
selector: 'app-protected-content',
standalone: true,
template: `
<div *ngIf="user$ | async as user">
<p>Welcome, {{ user.email }}!</p>
<button (click)="signOut()">Sign Out</button>
</div>
`,
styleUrls: ['./profile.component.css'],
imports: [AsyncPipe, CommonModule]
})
export class ProtectedContentComponent implements OnInit {
router: Router = inject(Router);
authService: AuthService = inject(AuthService);
user$ = this.authService.user$;
ngOnInit(): void {}
async signOut() {
try {
await this.authService.logout();
console.log('User signed out');
this.router.navigateByUrl('/');
} catch (error) {
console.error('Sign out error:', error);
// Handle the error appropriately, e.g., show a message to the user
}
}
}
This guide walks you through the core aspects of integrating Firebase Authentication into an Angular app. By applying these approaches, you’ll be able to protect your application and deliver an effortless user journey. Always weigh the security ramifications of authentication and treat user data with care. As you move forward, dive into the more sophisticated options Firebase Authentication offers to customize the login experience according to your requirements.
😄 Honestly, I’m thrilled about how straightforward this feature is to implement.
Feel free to connect with me on GitHub, where I’m working on interesting projects.
Thanks for reading, and please leave a ❤️ if you liked it.
See you later 👋
