How to implement guards in Angular

Angular guards control access to routes and navigation flow with interfaces that intercept routing decisions. As the creator of CoreUI with 12 years of Angular development experience, I’ve implemented guard strategies in production Angular applications that protect sensitive routes and manage complex authorization logic for enterprise applications serving millions of users.

The most secure approach uses functional guards (Angular 15+) for authentication with role-based access control.

Functional Guard (Angular 15+)

import { inject } from '@angular/core'
import { Router } from '@angular/router'
import { AuthService } from './auth.service'

export const authGuard = () => {
  const authService = inject(AuthService)
  const router = inject(Router)

  if (authService.isAuthenticated()) {
    return true
  }

  return router.createUrlTree(['/login'])
}

Usage in Routes

import { Routes } from '@angular/router'
import { authGuard } from './guards/auth.guard'

export const routes: Routes = [
  { path: 'login', component: LoginComponent },
  { path: 'dashboard', component: DashboardComponent, canActivate: [authGuard] },
  { path: 'profile', component: ProfileComponent, canActivate: [authGuard] },
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [authGuard, adminGuard]
  }
]

Role-Based Guard

import { inject } from '@angular/core'
import { Router } from '@angular/router'
import { AuthService } from './auth.service'

export const roleGuard = (allowedRoles: string[]) => {
  return () => {
    const authService = inject(AuthService)
    const router = inject(Router)

    const userRole = authService.getUserRole()

    if (allowedRoles.includes(userRole)) {
      return true
    }

    return router.createUrlTree(['/forbidden'])
  }
}

// Usage
export const routes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [roleGuard(['admin', 'superadmin'])]
  },
  {
    path: 'moderator',
    component: ModeratorComponent,
    canActivate: [roleGuard(['moderator', 'admin'])]
  }
]

Async Guard with Token Validation

import { inject } from '@angular/core'
import { Router } from '@angular/router'
import { AuthService } from './auth.service'

export const asyncAuthGuard = async () => {
  const authService = inject(AuthService)
  const router = inject(Router)

  try {
    const isValid = await authService.validateToken()

    if (isValid) {
      return true
    }

    return router.createUrlTree(['/login'])
  } catch (error) {
    console.error('Token validation failed:', error)
    return router.createUrlTree(['/login'])
  }
}

CanDeactivate Guard

import { inject } from '@angular/core'

export interface CanComponentDeactivate {
  canDeactivate: () => boolean | Promise<boolean>
}

export const unsavedChangesGuard = (component: CanComponentDeactivate) => {
  return component.canDeactivate
    ? component.canDeactivate()
    : true
}

// Component implementation
import { Component } from '@angular/core'
import { CanComponentDeactivate } from './guards/unsaved-changes.guard'

@Component({
  selector: 'app-edit-form',
  template: `
    <form>
      <input [(ngModel)]="formData" name="data" />
      <button (click)="save()">Save</button>
    </form>
  `
})
export class EditFormComponent implements CanComponentDeactivate {
  formData = ''
  hasUnsavedChanges = false

  canDeactivate(): boolean {
    if (this.hasUnsavedChanges) {
      return confirm('You have unsaved changes. Do you want to leave?')
    }
    return true
  }

  save() {
    this.hasUnsavedChanges = false
  }
}

Permission-Based Guard

import { inject } from '@angular/core'
import { Router, ActivatedRouteSnapshot } from '@angular/router'
import { PermissionService } from './permission.service'

export const permissionGuard = () => {
  return (route: ActivatedRouteSnapshot) => {
    const permissionService = inject(PermissionService)
    const router = inject(Router)

    const requiredPermission = route.data['permission']

    if (permissionService.hasPermission(requiredPermission)) {
      return true
    }

    return router.createUrlTree(['/forbidden'])
  }
}

// Usage
export const routes: Routes = [
  {
    path: 'users/create',
    component: CreateUserComponent,
    canActivate: [permissionGuard()],
    data: { permission: 'users.create' }
  },
  {
    path: 'users/:id/edit',
    component: EditUserComponent,
    canActivate: [permissionGuard()],
    data: { permission: 'users.edit' }
  }
]

Guard with Redirect

import { inject } from '@angular/core'
import { Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'
import { AuthService } from './auth.service'

export const redirectGuard = () => {
  return (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
    const authService = inject(AuthService)
    const router = inject(Router)

    if (authService.isAuthenticated()) {
      return true
    }

    return router.createUrlTree(['/login'], {
      queryParams: { returnUrl: state.url }
    })
  }
}

// Login component handling return URL
import { Component, OnInit } from '@angular/core'
import { Router, ActivatedRoute } from '@angular/router'
import { AuthService } from './auth.service'

@Component({
  selector: 'app-login',
  template: `
    <form (ngSubmit)="login()">
      <input [(ngModel)]="username" name="username" />
      <input [(ngModel)]="password" type="password" name="password" />
      <button type="submit">Login</button>
    </form>
  `
})
export class LoginComponent implements OnInit {
  username = ''
  password = ''
  returnUrl = '/'

  constructor(
    private authService: AuthService,
    private router: Router,
    private route: ActivatedRoute
  ) {}

  ngOnInit() {
    this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'
  }

  async login() {
    try {
      await this.authService.login(this.username, this.password)
      this.router.navigateByUrl(this.returnUrl)
    } catch (error) {
      console.error('Login failed:', error)
    }
  }
}

Best Practice Note

This is the same guard architecture we use in CoreUI’s Angular admin templates. Guards provide centralized access control for routes, preventing unauthorized access and improving security. Always use functional guards for new applications (Angular 15+), handle async validation properly, and combine guards for complex authorization requirements. Store sensitive authorization checks server-side and use guards primarily for UI/UX flow control.

For production applications, consider using CoreUI’s Angular Admin Template which includes pre-configured guard patterns with authentication, authorization, and permission management.

For complete routing implementation, check out how to use Angular Router and how to implement authentication in Angular.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.

Answers by CoreUI Core Team