Angular 2 authentication, restricting access to protected routes, showing alert message


How to implement protected routes based on user’s authentication status in Angular 2? Angular 2 authentication is a good intro to setting up a protected route but is missing two common things for web applications these days: redirecting a user to route which will help him gain access to protected route, most likely to a login page, and showing some kind of “flash message” to indicate why he’s denied an access to protected route.

First thing is easy. We only need to pass router instance to LoggedInGuard class and than if user isn’t logged in we redirect him to desired route while still returning login status from a method.

//logged-in.guard.ts
import {CanActivate, Router} from "@angular/router";
import {Injectable} from "@angular/core";
import {UserService} from "./user.service";

@Injectable()
export class LoggedInGuard implements CanActivate {
  constructor(private userService:UserService, private router:Router) {}

  canActivate() {
    const isLoggedIn = this.userService.isLoggedIn()
    if (!isLoggedIn) {
      this.router.navigate(['login'])
    }
    return isLoggedIn
  }
}

Solution to second problem is subscribing to router changes (it’s an observable) and setting the message according to change event. If change event is of type NavigationCancel than it’s because user is not authorized to access the page and is thrown to route specified in previous snippet and there a message about “logging in before continuing” can be shown. We can use NavigationEnd to clear that message when navigating to a different route and thus mimicking a flash message.

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'auth-app',
  template: `
  <div class="container body-container">
    <p *ngIf="!!message">{{message}}</p>
    <router-outlet></router-outlet>
  </div>
  `
})
export class AppComponent {
  message:string

  constructor(private router:Router) {
    router.events.filter(type => type instanceof NavigationCancel)
      .subscribe(() => {
        this.message = 'Log in in order to continue'
      })
    router.events.filter(type => type instanceof NavigationEnd)
      .subscribe(() => {
        this.message = ''
      })
  }
}