There is almost no application out there without authentication and restricted areas, right? With this article I will explain you how to handle restricted routes in Angular with Guards and Http requests, the real life way.

Authentication

The whole authentication logic will be kept in a separated module called AuthModule. Let’s begin by creating it.

$ ng generate module auth

This command creates a folder named auth under the app directory and it contains a TypeScript file named auth.module.ts

We will need an authentication service, so let’s create it. Through this service our application will communicate with our database (or a file in this case) to complete the login process and to give us the login status.

$ ng generate service auth/auth --flat

--flat flag indicates that no new directory will be created

These will be the contents of auth.service.ts file.

// auth.service.ts import { Injectable } from '@angular/core'; @Injectable({

providedIn: 'root'

}) export class AuthService {

constructor() { }

}

It’s time to create a new method called login that takes email and password as parameters, sends a POST HTTP request in a server and receives a response of whether the user exists or not. This information will be stored in a BehaviorSubject .

After that we will create a method called isLoggedIn that sends a GET HTTP request in a server and receives a response of whether the user is authenticated or not. I will explain later in this post why we need this.

// auth.service.ts ...

import { HttpClient, HttpHeaders} from "@angular/common/http"; import { Observable, BehaviorSubject } from "rxjs";

... isLogged: BehaviorSubject<boolean>; constructor(private http: HttpClient){} ... login(email: String, password: String): Observable<boolean> {

var body = {};

body['email'] = email;

body['password'] = password;

const headers = new HttpHeaders({'Content-Type': 'application/json'}); return this.http.post(environment.LOGIN_URL,

body,

{headers: headers}

).pipe(

map((response: any) => {

this.isLogged.next(response);

return response;

}

));

}

isLoggedIn() {

return this.http.get(environment.IS_LOGGEDIN_URL, {withCredentials: true}).pipe(

map(

(response: any) => {

this.isLogged.next(response);

return response;

}

));

} ...

At the terminal, execute the following command, to create a new component.

$ ng generate component auth/login --flat --module auth

--module auth option indicates in which module the new component will be declared

In this point we will use Reactive Forms to build our login form, and set the validators needed.

// login.component.ts import { Component, OnInit, OnDestroy } from "@angular/core";

import { FormGroup, FormControl, Validators } from "@angular/forms";

import { Router } from "@angular/router";

import { Subscription } from 'rxjs';

import { AuthService } from "./auth.service"; @Component({

selector: 'app-login',

templateUrl: './login.component.html'

}) export class LoginComponent implements OnInit, OnDestroy{ loginForm: FormGroup;

subscription: Subscription; constructor(private authService: AuthService,

private router: Router) {} onSubmit() {

this.subscription = this.authService.login(

this.myForm.value.email,

this.loginForm.value.password)

.subscribe(

(response: any) => {

this.router.navigate(['/new']);

}

);

} ngOnInit() {

this.loginForm = new FormGroup({

email: new FormControl(null, [

Validators.required,

Validators.pattern("[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?")

]),

password: new FormControl(null, Validators.required)

});

} ngOnDestroy() {

if(this.subscription) this.subscription.unsubscribe();

}

}

As you can see above, in onSubmit method we will handle the button click event in order to get user information and continue with the login process. If the user logs in successfully, then he/she will be redirected at the /new route.

To complete the login process, we need to write the template I mentioned above. The template file login.component.html will contain a very basic HTML structure: a form with two inputs (email and password) and a button. No fancy things here! (but you can add any style you want)

<!-- login.component.html --> <form [formGroup]="loginForm" (ngSubmit)="onSubmit()">

<div>

<label for="email">Email</label>

<input type="email" id="email" formControlName="email">

</div>

<div>

<label for="password">Password</label>

<input type="password" id="password" formControlName="password">

</div>

<button type="submit" [disabled]="!loginForm.valid">Log me in</button>

</form>

Protecting Routes

Firstly, we assume that the component we want to protect is called ProtectedComponent and it belongs in AppModule

The app-routing.module.ts file contains the application’s routes and their permissions.

// app-routing.module.ts import { Routes, RouterModule } from "@angular/router";

import { AppComponent } from './app.component';

import { HomeComponent } from './home.component';

import { ProtectedComponent } from './home.component';

import { LoginComponent } from './auth/login.component';

import { LoggedInGuard } from "./auth/logged-in.guard";

import { GuestGuard } from "./auth/guest.guard"; export const APP_ROUTES: Routes = [

{ path: '', component: AppComponent, children: [

{ path: '',

component: HomeComponent

},

{ path: 'login',

component: LoginComponent,

canActivate: [GuestGuard]

},

{

path: 'protected',

component: ProtectedComponent, canActivate: [LoggedInGuard]

}

]}

];

export const routing = RouterModule.forRoot(APP_ROUTES);

As we see above, / route is accessible by anyone, /login route is accessible by guests and /protected route is accessible by logged-in users.

From the very first words I put the term Guards on the table, but what do they really mean?

By definition, Guards are interfaces that can be implemented in different ways, but they return either a Promise<boolean> , or an Observable<boolean> or a boolean . With Angular v.7.1 a UrlTree , that indicates the new router state that should be activated, can be returned instead.

In practice, as the name suggests, they allow us to guard access to a certain route.

There are four types of Guards available:

1. CanActivate : decides if a route can be activated

2. CanActivateChild : decides if child routes in a route can be activated

3. CanDeactivate : decides if a route can be deactivated

4. CanLoad : decides if child routes in a route can be loaded

In this example, we need to implement two guards:

GuestGuard : returns whether a user is guest or not LoggedinGuard : returns whether a user is logged in or not

I am starting with the GuestGuard . In this case we want to activate the route if the user is guest. Otherwise, we want to redirect him/her at the protected route.

// guest.guard.ts

import { CanActivate, Router } from '

import { Observable } from 'rxjs';

import { AuthService } from './auth.service'; import { Injectable } from ' @angular/core ';import { CanActivate, Router } from ' @angular/router ';import { Observable } from 'rxjs';import { AuthService } from './auth.service';

export class GuestGuard implements CanActivate { @Injectable ()export class GuestGuard implements CanActivate { constructor(private authService: AuthService, private router: Router) {} canActivate(): Observable<boolean> | Promise<boolean> | boolean {

return this.authService.isLogged.pipe(map(logged => {

if(logged) {

this.router.navigate(['/protected']);

return false;

}

return true;

})

)

}

}

In LoggedInGuard we want to activate the route if the user is logged in. Otherwise, we want to redirect him/her at the login route.

// logged-in.guard.ts

import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '

import { Observable } from 'rxjs'; import { Injectable } from ' @angular/core ';import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from ' @angular/router ';import { Observable } from 'rxjs'; import { AuthService } from './auth.service';

export class LoggedInGuard implements CanActivate { @Injectable ()export class LoggedInGuard implements CanActivate { constructor(private authService: AuthService, private router: Router) {} canActivate(): Observable<boolean> | Promise<boolean> | boolean {

return this.authService.isLogged.pipe(map(logged => {

if(!logged) {

this.router.navigate(['login']);

return false;

}

return true;

})

)

}

}

Now, we must provide these guards in AppModule .

// app.module.ts @NgModule({

...

providers: [..., GuestGuard, LoggedInGuard]

...

})

Final Configuration

We have to do two more things to make it work perfectly:

Create AppService that contains the initiallizeApp method Import AuthModule in AppModule Run initiallizeApp in APP_INITIALIZER

// app.service.ts

import { AuthService } from './auth/auth.service'; import { Injectable, Injector } from ' @angular/core ';import { AuthService } from './auth/auth.service';

export class AppService { @Injectable ()export class AppService { constructor(private injector: Injector) {} initializeApp(): Promise<any> {

return new Promise(((resolve, reject) => {

this.injector.get(AuthService).isLoggedIn()

.toPromise()

.then(res => {

resolve();

})

}))

}

}

At the beginning of this post, I mentioned that we need isLoggedIn method for some reason.

Let’s think about this case: A user signs in and he/she is successfully redirected to the protected route. And in that moment he/she decides to reload the page. When the page loads again, the authentication information we stored in the BehaviorSubject no longer exists. So, we need to ask our server about the authentication status of our user again to regain this information.

And when it’s the right moment to call this method? At the initialization of our application of course! Before anything else is loaded.

How can we do this?

There is a function called APP_INITIALIZER that does exactly what we want.

By definition, APP_INITIALIZER is a function that will be executed when an application is initialized.

It is imported from @angular/core

// app.module.ts import { ..., APP_INITIALIZER } from '@angular/core';

import { AppService } from './app.service'; ...

export function app_init(appService: AppService) {

return () => appService.initializeApp();

} @NgModule({

...

imports: [

...,

AuthModule

],

providers: [

...,

AppService,

{

provide: APP_INITIALIZER, useFactory: app_init, deps: [AppService], multi: true

}

]

...

})

...

There can be multiple APP_INITIALIZER functions. They are run concurrently and the initialization process is complete when they all finish.

The only thing remains to be is to build and run your application!

Wrapping Up

Hopefully you have been able to see the benefits of using Route Guards to protect the accessibility of your application’s routes.

Thank you for reading!