Hey there! Remember when I built that user dashboard for my blog project and realised anyone could just type in the admin URL and see all the sensitive stuff? That panicked moment taught me a valuable lesson—routes need protection! That's exactly what Angular's CanActivate guard does: it acts like a bouncer at a club, checking if users have the right credentials before letting them into protected areas of your app.
The Problem I Faced (And Solved)
How I Built My CanActivate Guard
Step 1: Create the Auth Service (The Brain)
// auth.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class AuthService {
private loggedInSubject = new BehaviorSubject<boolean>(false);
loggedIn$ = this.loggedInSubject.asObservable();
login(email: string, password: string): boolean {
// Simulate API call
if (email === 'admin@codevichar.com' && password === 'codevichar123') {
localStorage.setItem('token', 'fake-jwt-token');
this.loggedInSubject.next(true);
return true;
}
return false;
}
logout(): void {
localStorage.removeItem('token');
this.loggedInSubject.next(false);
}
isLoggedIn(): boolean {
return !!localStorage.getItem('token');
}
}
Step 2: Build the CanActivate Guard (The Bouncer)
// admin.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AuthService } from './auth.service';
import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class AdminGuard implements CanActivate {
constructor(
private authService: AuthService,
private router: Router
) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> {
return this.authService.loggedIn$.pipe(
tap(_isLoggedIn => {
if (!_isLoggedIn) {
// Redirect to login with return URL
this.router.navigate(['/login'], {
queryParams: { returnUrl: state.url }
});
}
}),
map(isLoggedIn1 => isLoggedIn1)
);
}
}
Notice the tap operator? It lets me redirect without affecting the boolean return value. Clean!
Step 3: Wire It to Routes
// app-routing.module.ts
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'login', component: LoginComponent },
{
path: 'admin',
component: AdminDashboardComponent,
canActivate: [AdminGuard]
},
{ path: '**', redirectTo: '' }
];
Step 4: Login Component (Bonus)
// login.component.ts
export class LoginComponent implements OnInit {
email = '';
password = '';
error = '';
constructor(
private authService: AuthService,
private router: Router
) {}
onLogin() {
if (this.authService.login(this.email, this.password)) {
this.router.navigate(['/admin']);
} else {
this.error = 'Wrong credentials recieved!';
}
}
}
What Makes This Setup Awesome
- Early Protection: Blocks access before the component even initialises
- Smart Redirects: Sends users to login with their intended destination preserved
- Async Ready: Works with Observables/Promises for real API calls
- Multiple Guards: Stack several (auth + role + feature flag)
- Clean Components: No auth logic cluttering your pages
Pro tip: Always return UrlTree from Router for programmatic redirects instead of just navigating—it cancels navigation cleanly.
When I first implemented this for codevichar.com, it felt like adding a proper lock to my digital house. No more heart attacks about open admin pages!