|
| 1 | +# Control route access with guards |
| 2 | + |
| 3 | +CRITICAL: Never rely on client-side guards as the sole source of access control. All JavaScript that runs in a web browser can be modified by the user running the browser. Always enforce user authorization server-side, in addition to any client-side guards. |
| 4 | + |
| 5 | +Route guards are functions that control whether a user can navigate to or leave a particular route. They are like checkpoints that manage whether a user can access specific routes. Common examples of using route guards include authentication and access control. |
| 6 | + |
| 7 | +## Creating a route guard |
| 8 | + |
| 9 | +You can generate a route guard using the Angular CLI: |
| 10 | + |
| 11 | +```bash |
| 12 | +ng generate guard CUSTOM_NAME |
| 13 | +``` |
| 14 | + |
| 15 | +This will prompt you to select which [type of route guard](#types-of-route-guards) to use and then create the corresponding `CUSTOM_NAME-guard.ts` file. |
| 16 | + |
| 17 | +TIP: You can also create a route guard manually by creating a separate TypeScript file in your Angular project. Developers typically add a suffix of `-guard.ts` in the filename to distinguish it from other files. |
| 18 | + |
| 19 | +## Route guard return types |
| 20 | + |
| 21 | +All route guards share the same possible return types. This gives you flexibility in how you control navigation: |
| 22 | + |
| 23 | +| Return types | Description | |
| 24 | +| ------------------------------- | --------------------------------------------------------------------------------- | |
| 25 | +| `boolean` | `true` allows navigation, `false` blocks it (see note for `CanMatch` route guard) | |
| 26 | +| `UrlTree` or `RedirectCommand` | Redirects to another route instead of blocking | |
| 27 | +| `Promise<T>` or `Observable<T>` | Router uses the first emitted value and then unsubscribes | |
| 28 | + |
| 29 | +Note: `CanMatch` behaves differently— when it returns `false`, Angular tries other matching routes instead of completely blocking navigation. |
| 30 | + |
| 31 | +## Types of route guards |
| 32 | + |
| 33 | +Angular provides four types of route guards, each serving different purposes: |
| 34 | + |
| 35 | +<docs-pill-row> |
| 36 | + <docs-pill href="#canactivate" title="CanActivate"/> |
| 37 | + <docs-pill href="#canactivatechild" title="CanActivateChild"/> |
| 38 | + <docs-pill href="#candeactivate" title="CanDeactivate"/> |
| 39 | + <docs-pill href="#canmatch" title="CanMatch"/> |
| 40 | +</docs-pill-row> |
| 41 | + |
| 42 | +### CanActivate |
| 43 | + |
| 44 | +The `CanActivate` guard determines whether a user can access a route. It is most commonly used for authentication and authorization. |
| 45 | + |
| 46 | +It has access to the following default arguments: |
| 47 | + |
| 48 | +- `route: ActivatedRouteSnapshot` - Contains information about the route being activated |
| 49 | +- `state: RouterStateSnapshot` - Contains the router's current state |
| 50 | + |
| 51 | +It can return the [standard return guard types](#route-guard-return-types). |
| 52 | + |
| 53 | +```ts |
| 54 | +export const authGuard: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { |
| 55 | + const authService = inject(AuthService); |
| 56 | + return authService.isAuthenticated(); |
| 57 | +}; |
| 58 | +``` |
| 59 | + |
| 60 | +Tip: If you need to redirect the user, return a [`URLTree`](api/router/UrlTree) or [`RedirectCommand`](api/router/RedirectCommand). Do **not** return `false` and then programmatically `navigate` the user. |
| 61 | + |
| 62 | +For more information, check out the [API docs for CanActivateFn](api/router/CanActivateFn). |
| 63 | + |
| 64 | +### CanActivateChild |
| 65 | + |
| 66 | +The `CanActivateChild` guard determines whether a user can access child routes of a particular parent route. This is useful when you want to protect an entire section of nested routes. In other words, `canActivateChild` runs for _all_ children. If there is a child component with another child component underneath of it, `canActivateChild` will run once for both components. |
| 67 | + |
| 68 | +It has access to the following default arguments: |
| 69 | + |
| 70 | +- `childRoute: ActivatedRouteSnapshot` - Contains information about the "future" snapshot (i.e., state the router is attempting to navigate to) of the child route being activated |
| 71 | +- `state: RouterStateSnapshot` - Contains the router's current state |
| 72 | + |
| 73 | +It can return the [standard return guard types](#route-guard-return-types). |
| 74 | + |
| 75 | +```ts |
| 76 | +export const adminChildGuard: CanActivateChildFn = (childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { |
| 77 | + const authService = inject(AuthService); |
| 78 | + return authService.hasRole('admin'); |
| 79 | +}; |
| 80 | +``` |
| 81 | + |
| 82 | +For more information, check out the [API docs for CanActivateChildFn](api/router/CanActivateChildFn). |
| 83 | + |
| 84 | +### CanDeactivate |
| 85 | + |
| 86 | +The `CanDeactivate` guard determines whether a user can leave a route. A common scenario is preventing navigation away from unsaved forms. |
| 87 | + |
| 88 | +It has access to the following default arguments: |
| 89 | + |
| 90 | +- `component: T` - The component instance being deactivated |
| 91 | +- `currentRoute: ActivatedRouteSnapshot` - Contains information about the current route |
| 92 | +- `currentState: RouterStateSnapshot` - Contains the current router state |
| 93 | +- `nextState: RouterStateSnapshot` - Contains the next router state being navigated to |
| 94 | + |
| 95 | +It can return the [standard return guard types](#route-guard-return-types). |
| 96 | + |
| 97 | +```ts |
| 98 | +export const unsavedChangesGuard: CanDeactivateFn<FormComponent> = (component: FormComponent, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState: RouterStateSnapshot) => { |
| 99 | + return component.hasUnsavedChanges() |
| 100 | + ? confirm('You have unsaved changes. Are you sure you want to leave?') |
| 101 | + : true; |
| 102 | +}; |
| 103 | +``` |
| 104 | + |
| 105 | +For more information, check out the [API docs for CanDeactivateFn](api/router/CanDeactivateFn). |
| 106 | + |
| 107 | +### CanMatch |
| 108 | + |
| 109 | +The `CanMatch` guard determines whether a route can be matched during path matching. Unlike other guards, rejection falls through to try other matching routes instead of blocking navigation entirely. This can be useful for feature flags, A/B testing, or conditional route loading. |
| 110 | + |
| 111 | +It has access to the following default arguments: |
| 112 | + |
| 113 | +- `route: Route` - The route configuration being evaluated |
| 114 | +- `segments: UrlSegment[]` - The URL segments that have not been consumed by previous parent route evaluations |
| 115 | + |
| 116 | +It can return the [standard return guard types](#route-guard-return-types), but when it returns `false`, Angular tries other matching routes instead of completely blocking navigation. |
| 117 | + |
| 118 | +```ts |
| 119 | +export const featureToggleGuard: CanMatchFn = (route: Route, segments: UrlSegment[]) => { |
| 120 | + const featureService = inject(FeatureService); |
| 121 | + return featureService.isFeatureEnabled('newDashboard'); |
| 122 | +}; |
| 123 | +``` |
| 124 | + |
| 125 | +It can also allow you to use different components for the same path. |
| 126 | + |
| 127 | +```ts |
| 128 | +// 📄 routes.ts |
| 129 | +const routes: Routes = [ |
| 130 | + { |
| 131 | + path: 'dashboard', |
| 132 | + component: AdminDashboard, |
| 133 | + canMatch: [adminGuard] |
| 134 | + }, |
| 135 | + { |
| 136 | + path: 'dashboard', |
| 137 | + component: UserDashboard, |
| 138 | + canMatch: [userGuard] |
| 139 | + } |
| 140 | +] |
| 141 | +``` |
| 142 | + |
| 143 | +In this example, when the user visits `/dashboard`, the first one that matches the correct guard will be used. |
| 144 | + |
| 145 | +For more information, check out the [API docs for CanMatchFn](api/router/CanMatchFn). |
| 146 | + |
| 147 | +## Applying guards to routes |
| 148 | + |
| 149 | +Once you've created your route guards, you need to configure them in your route definitions. |
| 150 | + |
| 151 | +Guards are specified as arrays in the route configuration in order to allow you to apply multiple guards to a single route. They are executed in the order they appear in the array. |
| 152 | + |
| 153 | +```ts |
| 154 | +import { Routes } from '@angular/router'; |
| 155 | +import { authGuard } from './guards/auth.guard'; |
| 156 | +import { adminGuard } from './guards/admin.guard'; |
| 157 | +import { canDeactivateGuard } from './guards/can-deactivate.guard'; |
| 158 | +import { featureToggleGuard } from './guards/feature-toggle.guard'; |
| 159 | + |
| 160 | +const routes: Routes = [ |
| 161 | + // Basic CanActivate - requires authentication |
| 162 | + { |
| 163 | + path: 'dashboard', |
| 164 | + component: DashboardComponent, |
| 165 | + canActivate: [authGuard] |
| 166 | + }, |
| 167 | + |
| 168 | + // Multiple CanActivate guards - requires authentication AND admin role |
| 169 | + { |
| 170 | + path: 'admin', |
| 171 | + component: AdminComponent, |
| 172 | + canActivate: [authGuard, adminGuard] |
| 173 | + }, |
| 174 | + |
| 175 | + // CanActivate + CanDeactivate - protected route with unsaved changes check |
| 176 | + { |
| 177 | + path: 'profile', |
| 178 | + component: ProfileComponent, |
| 179 | + canActivate: [authGuard], |
| 180 | + canDeactivate: [canDeactivateGuard] |
| 181 | + }, |
| 182 | + |
| 183 | + // CanActivateChild - protects all child routes |
| 184 | + { |
| 185 | + path: 'users', // /user - NOT protected |
| 186 | + canActivateChild: [authGuard], |
| 187 | + children: [ |
| 188 | + // /users/list - PROTECTED |
| 189 | + { path: 'list', component: UserListComponent }, |
| 190 | + // /useres/detail/:id - PROTECTED |
| 191 | + { path: 'detail/:id', component: UserDetailComponent } |
| 192 | + ] |
| 193 | + }, |
| 194 | + |
| 195 | + // CanMatch - conditionally matches route based on feature flag |
| 196 | + { |
| 197 | + path: 'beta-feature', |
| 198 | + component: BetaFeatureComponent, |
| 199 | + canMatch: [featureToggleGuard] |
| 200 | + }, |
| 201 | + |
| 202 | + // Fallback route if beta feature is disabled |
| 203 | + { |
| 204 | + path: 'beta-feature', |
| 205 | + component: ComingSoonComponent |
| 206 | + } |
| 207 | +]; |
| 208 | +``` |
0 commit comments