How To Protect Routes In Angular

A great way to take your app to the next level is to implement Guards, which are interfaces that protect routes and grant access based on defined permissions. This is useful for apps with an admin dashboard.

Think of a CMS like WordPress where a user can access the dashboard once logged in.

Check out the final application below!


What You Will Learn

  • What Guards Are

  • Why Use Guards

  • How To Create A Guard

  • How To Integrate A Guard To A Route

Create An App

Using the CLI make a new app, in this example <app-name> will be admin-app:

Once our new app is generated run the app:

or

Now that we know our app is working we can begin to build our components.

Create Components

We need two components:

  • Dashboard - Our component we'll protect from users with no permission.

  • Login - Our component that will grant user permission to access the dashboard component.

Generate the two components:

The CLI will automatically add our components to the appModule.

angular/router-guards/code/admin-app/src/app/app.module.ts

Setup Routing

Let's create a routing module to handle what components a user will see when browsing our app.

Create a module named app-routing

--flat option will only generate a file instead of a folder. and it will be on the base level.

--module option is to tell the cli which module to register the new module to. in this example it is the 'app' module.

The generated file app-routing.module.ts will look like this:

Create an Array of Routes

In the app-routing.module.ts file import the Login and Dashboard components. Also import the RouterModule, and Routes from @angular/router library.

angular/router-guards/code/admin-app/src/app/app-routing.module.ts

Now that we have our components imported next create an array of objects named routes of type Routes. Each object is a route with a path and a component to render to that path.

keep in mind the order of the paths matters as the first one matched will be rendered.

Our path value is which url path the component will render to. An empty path '' is used alongside the property redirectTo with a value of /login and the property pathMatch with the value full to tell the browser to re-direct the user to the login path if the url path is empty.

Implement the RouterModule

We want our router to be configured at the app's root level. To do so we'll use the forRoot() method from RouterModule.

Inject the RouterModule to the @NgModule() metadata imports array and pass the routes array into forRoot():

Export RouterModule so that the AppModule can utilize the router-outlet directive and other directives including service providers anywhere in the app.

angular/router-guards/code/admin-app/src/app/app-routing.module.ts

The app-routing.module.ts file will look like this completed.

Notice the CommonModule and declarations are removed since they are unused. It's good practice to remove what you don't use. This keeps code bloat to a minimum.

Make sure the AppModule has the AppRoutingModule imported. The file should look like this.

If we visit the browser and navigate to our paths notice that nothing changes. Why is that?

We've missed one crucial step, which is implementing the router-outlet directive.

Update App Component

Let's open the app.component.html file, replace all its content with the router-outlet and simple navigation to our login and dashboard routes.

The app's template should look like this:

angular/router-guards/code/admin-app/src/app/app.component.html

we are able to use routerLink thanks to the RouterModule

Once that's done we can visit each of our paths directly in the browser.

The next thing to do is to update the Login component.

Update Login Component

We can now see our components render to the browser. Let's stub out functionality. Stubbing is like blueprinting. We add placeholder functionality as a guide which helps in debugging.

What we want is for the Login component to handle access to our dashboard by granting the user permission via a guard (more on this later).

Update the login.component.ts file:

Update the login.component.html file:

angular/router-guards/code/admin-app/src/app/login/login.component.html

What we've done is to interpolate a simple message that is attached to the two buttons click events that call our methods: login() and logout()




We'll revisit the Login component later to integrate the guard we're creating next.

Create A Guard

Guards are interfaces that return a Promise< boolean >, Observable< boolean >, a boolean, or a UrlTree.

There are multiple types of guards:

  • CanActivate -Controls access to a route.

  • CanActivateChild - Controls access to a child route.

  • CanDeactivate - Mediate navigation away from the current route.

  • Resolve - Perform route data retrieval before route activation.

  • CanLoad - Mediate navigation to a feature module loaded async.

Create a new guard named auth inside a new folder, auth.

By default our newly generated guard implements the CanActivate guard interface.

The file auth.guard.ts contains a method canActivate() that accepts the parameters: next, and state and returns a boolean.

Go to the app-routing.module.ts file and import the newly created guard to our routing module.

Tie the imported guard to the route we want protected which is the dashboard route.

angular/router-guards/code/admin-app/src/app/app-routing.module.ts


Next we need to update our guard with specific conditions. These conditions we'll create through a service.

Create A Service

we are making a service to mock authentication conditions. our guard will call the service to login a user and retain that user's information.

generate a service named auth inside a folder named auth.

We now have a bare-bones service file.

Update the service:

We have created a login method as an observable, a logout method, a boolean flag isLoggedIn and a string variable redirectUrl.

Import the operators tap and delay from rxjs/operators.

Update the login method to return an emitted value of true. This will simulate an API call.

Add a pipe method and use tap after a 1sec delay to toggle the isLoggedIn flag to true.

In the logout method toggle the isLoggedIn flag to false.

auth.service.ts should now look like this:

angular/router-guards/code/admin-app/src/app/auth/auth.service.ts


Our service is now ready to perform a login authentication. Let's implement it in our guard.

Update Guard

Now that we have a service we can use its functionality in our guard.

Import our newly created AuthService and inject it in the constructor along with Router.

Update the auth.guard.ts file to this:


angular/router-guards/code/admin-app/src/app/app-routing.module.ts


The changes made are:

  • A new method checkLogin() which accepts a url string. The method returns true if the isLoggedIn flag from the AuthService is true, sets the redirectUrl from the AuthService to the passed in url then navigates to the login path and returns false.

  • The canActivate() method now returns a call to checkLogin() which accepts a url string from the parameter state which will be the previous route the user came from if the guard check is false, not permitting the user to be logged in.

we can now update our login component and tie in the new functionality.

Update Login Component Again

Currently our file login.component.ts looks like this: