How to Implement the Auth Guard in Angular: Class-Based and Functional Approaches
An Angular auth guard is a route guard that implements the CanActivate interface or uses a CanActivateFn function to return true, false, or a UrlTree that controls navigation access based on authentication status.
The Angular router provides a declarative guard system to intercept and control navigation based on runtime conditions. According to the angular/angular source code, the router evaluates these guards during the navigation lifecycle to determine whether to proceed, cancel, or redirect a requested route change.
Understanding the CanActivate Interface
The foundation of an auth guard lies in the CanActivate interface defined in packages/router/src/models.ts (lines 58-70). This contract requires a canActivate method that receives ActivatedRouteSnapshot and RouterStateSnapshot parameters and returns either a boolean, a UrlTree, or an asynchronous observable or promise of these types.
interface CanActivate {
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree;
}
Creating a Class-Based Auth Guard
The traditional approach involves creating an injectable service that implements the CanActivate interface. The guard injects an authentication service to check login status and uses the Router to create redirect URL trees when access is denied.
// src/app/auth/auth.guard.ts
import { Injectable } from '@angular/core';
import {
CanActivate,
Router,
UrlTree,
ActivatedRouteSnapshot,
RouterStateSnapshot,
} from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';
@Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> {
if (this.auth.isLoggedIn()) {
return true;
}
return this.router.createUrlTree(['/login'], {
queryParams: { returnUrl: state.url },
});
}
}
Implementing Functional Auth Guards (CanActivateFn)
Modern Angular applications increasingly use standalone functional guards instead of classes. The CanActivateFn type, also defined in packages/router/src/models.ts, represents a function that receives the route and state arguments directly. The router treats these uniformly with class-based guards through the mapping utilities in packages/router/src/utils/functional_guards.ts (specifically mapToCanActivate at lines 43-50).
// src/app/auth/auth.guard.ts
import { inject } from '@angular/core';
import { Router, UrlTree, CanActivateFn } from '@angular/router';
import { AuthService } from './auth.service';
export const authGuard: CanActivateFn = (route, state) => {
const auth = inject(AuthService);
const router = inject(Router);
return auth.isLoggedIn()
? true
: router.createUrlTree(['/login'], { queryParams: { returnUrl: state.url } });
};
Registering the Guard in Route Configuration
To activate the protection, add the guard to the canActivate array in your route configuration. The router executes these guards via runCanActivate in packages/router/src/operators/check_guards.ts (lines 72-99) before allowing navigation to proceed.
// src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './auth/auth.guard';
// import { authGuard } from './auth/auth.guard'; // For functional version
const routes: Routes = [
{
path: 'dashboard',
canActivate: [AuthGuard], // or [authGuard] for functional
loadChildren: () => import('./dashboard/dashboard.module')
.then(m => m.DashboardModule),
},
{ path: 'login', component: LoginComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
How the Router Evaluates Guard Return Values
According to the implementation in packages/router/src/operators/check_guards.ts (lines 149-176), the router interprets the guard's return value to determine the navigation outcome:
true: Navigation proceeds to the requested route.false: Navigation is cancelled entirely.UrlTree: The router cancels the current navigation and initiates a redirect to the specified URL tree.
This logic enables sophisticated access control patterns, including redirecting unauthenticated users to a login page while preserving the originally requested URL in query parameters.
Protecting Child Routes with CanActivateChild
For guarding nested route hierarchies, implement the CanActivateChild interface or use CanActivateChildFn. This guard runs before the router activates any child of the configured route, allowing you to protect entire feature modules with a single declaration.
// Class-based child guard
@Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivateChild {
constructor(private auth: AuthService, private router: Router) {}
canActivateChild(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> {
return this.auth.isLoggedIn() ? true : this.router.createUrlTree(['/login']);
}
}
Register it using canActivateChild in the route configuration:
{
path: 'admin',
canActivateChild: [AuthGuard],
children: [
{ path: 'users', component: UserManagementComponent },
{ path: 'settings', component: SettingsComponent },
],
}
Summary
- Auth guards in Angular implement the
CanActivateorCanActivateChildinterfaces, or use the functionalCanActivateFntype defined inpackages/router/src/models.ts. - Return values determine navigation fate:
trueallows access,falseblocks navigation, andUrlTreetriggers a redirect. - Functional guards use the
inject()function to access services and are mapped to the router's internal guard system viamapToCanActivateinpackages/router/src/utils/functional_guards.ts. - Registration occurs in the route configuration's
canActivateorcanActivateChildarrays, evaluated byrunCanActivateinpackages/router/src/operators/check_guards.ts. - Child route protection allows guarding entire feature modules without repeating guard declarations on each child.
Frequently Asked Questions
What is the difference between CanActivate and CanActivateFn?
CanActivate is a class-based interface requiring a canActivate method, while CanActivateFn is a functional type representing a standalone function. Both are defined in packages/router/src/models.ts and treated uniformly by the router, though functional guards reduce boilerplate and work naturally with standalone components.
How do I redirect to the login page when authentication fails?
Return a UrlTree created via router.createUrlTree(['/login']) instead of false. The router interprets this return value (as implemented in packages/router/src/operators/check_guards.ts lines 149-176) as a command to cancel the current navigation and redirect to the specified route.
Can auth guards handle asynchronous authentication checks?
Yes. The canActivate signature explicitly supports Observable<boolean | UrlTree> and Promise<boolean | UrlTree> return types. This allows you to verify tokens via HTTP requests or check asynchronous storage before rendering the protected component.
How do I protect all child routes under a parent path?
Use the CanActivateChild interface or CanActivateChildFn type and register the guard in the route configuration using canActivateChild: [YourGuard]. This runs the guard check before activating any child route within that parent path, protecting entire feature modules efficiently.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →