Check if the user logged in on any page change in Angular 2

I'm showing you simple implementation with Angular2. You can take advantage of @CanActivate hook as shown blow to check whether the user is loggedIn or not with isLoggedIn function which returns promise.

NOTE: below implementation is to check whether user is loggedIn before accessing any component or not. I hope by some modification you can achieve what you want to have.

Auth.ts

import {Observable} from 'rxjs/Observable';

export class Auth {
  constructor() {
    this.loggedIn = false;
  }

  login() {
    this.loggedIn = true;
  }

  logout() {
    this.loggedIn = false;
  }

  check() {
    return Observable.of(this.loggedIn);
  }
}

isLoggedIn.ts

import {Injector} from 'angular2/core';
import {appInjector} from './appInjector';
import {Auth} from './Auth';
import {Router, ComponentInstruction} from 'angular2/router';

export const isLoggedIn = (next: ComponentInstruction, previous: ComponentInstruction) => {
    let injector: Injector = appInjector(); // get the stored reference to the injector
    let auth: Auth = injector.get(Auth);
    let router: Router = injector.get(Router);

  // return a boolean or a promise that resolves a boolean
    return new Promise((resolve) => {
      auth.check()
          .subscribe((result) => {
                    if (result) {
                        resolve(true);
                    } else {
                        router.navigate(['/Login']);
                        resolve(false);
                    }
                });
  });
};

appInjector.ts

import {Injector} from 'angular2/core';

let appInjectorRef: Injector;
export const appInjector = (injector?: Injector):Injector => {
    if (injector) {
      appInjectorRef = injector;
    }

    return appInjectorRef;
};

somecomponent.ts

import {Component, View,ViewChild} from 'angular2/core';
import {CanActivate} from 'angular2/router';
import {isLoggedIn} from './isLoggedIn';

@Component({
  selector: 'some',
  template: 'some text'
})
@CanActivate((next: ComponentInstruction, previous: ComponentInstruction) => {
  return isLoggedIn(next, previous);  // this will tell whether user is loggedIn or not. 
})
export class Protected {
}

boot.ts

.
.
import { provide, ComponentRef } from 'angular2/core';
import { appInjector } from './app-injector';
.
.
bootstrap(AppComponent, [...]).then((appRef: ComponentRef) => {
  // store a reference to the application injector
  appInjector(appRef.injector);
});

There are two ways to restrict access Custom Router Outlet and CanActivate Decorator shown and implemented in this great article Authentication in Angular 2


This is what I did, used canActive property in app.routing.ts

 {
  path: 'dashboard',
  loadChildren: './dashboard',
  canActivate:[AuthGuard]
}, 

Please follow the below 5 minutes video tutorial

https://www.youtube.com/watch?v=0Qsg8fyKwO4

Note: This solution works fine for Angular 4


I think extending RouterOutlet is a common way to achive this

Example posted a while ago in Gitter by CaptainCodeman (not tested myself yet)

  import {Directive, Injector, Attribute, ElementRef, DynamicComponentLoader} from 'angular2/core';
  import {Router, RouteData, RouterOutlet, RouteParams, Instruction, ComponentInstruction} from 'angular2/router';

  /*
    Example implementation

    Given a route:
    @RouteConfig([
    { path: '/thing/:id', component: ThingComponent, name: 'Thing', data: { public: false, roles:['thinger'] } }
    ])

    authorize(instruction: ComponentInstruction):boolean {
      // simplest case - route is public
      if (<boolean>instruction.routeData.data['public']) {
        return true;
      }

      // if not public then we at least need an authenticated user
      if (this.isAuthenticated()) {
        var routeRoles = <any[]>instruction.routeData.data['roles'];
        var userRoles = <string[]>this.roles();

        // no roles required for route = user just needs to be authenticated
        var authorized = routeRoles.length === 0 || routeRoles.some(routeRole => userRoles.indexOf(routeRole) >= 0);

        return authorized;
      }

      return false;
    }
  */
  export abstract class IAuthService {
    abstract isAuthenticated():boolean;
    authorize(instruction: ComponentInstruction, params:any):boolean {
      // authorized if route allows public access or user is authenticated
      return this.isAuthenticated() || <boolean>instruction.routeData.data['public']
    }
  }
@Directive({selector: 'secure-outlet'})
  export class SecureRouterOutlet extends RouterOutlet {
    signin:string;
    unauthorized:string;
    injector:Injector;

    private parentRouter: Router;
    private authService: IAuthService;

    constructor(_elementRef: ElementRef, _loader: DynamicComponentLoader,
                _parentRouter: Router, @Attribute('name') nameAttr: string,
                authService:IAuthService,
                injector:Injector,
                @Attribute('signin') signinAttr: string,
                @Attribute('unauthorized') unauthorizedAttr: string) {
      super(_elementRef, _loader, _parentRouter, nameAttr);
      this.parentRouter = _parentRouter;
      this.authService = authService;
      this.injector = injector;
      this.signin = signinAttr;
      this.unauthorized = unauthorizedAttr;
    }

    activate(nextInstruction: ComponentInstruction): Promise<any> {
      var params = this.getAllRouteParams(this.injector);
      var isAuthorized = this.authService.authorize(nextInstruction, params);

      if (isAuthorized) {
        return super.activate(nextInstruction);
      }

      if (this.authService.isAuthenticated()) {
        var ins = this.parentRouter.generate([this.unauthorized]);
        return super.activate(ins.component);
      } else {
        var ins = this.parentRouter.generate([this.signin,{url:location.pathname}]);
        return super.activate(ins.component);
      }
    }

    reuse(nextInstruction: ComponentInstruction): Promise<any> {
      return super.reuse(nextInstruction);
    }

    getAllRouteParams(injector) {
      let params = null;
      while(injector) {
        const routeParams = injector.getOptional(RouteParams);
        if (routeParams) {
          if (params === null) {
            params = {};
          } else {
            params = Object.create(params);
          }

          Object.assign(params, routeParams.params);
        }
        injector = injector.parent;
      }
      return params;
    }
  }

If you use routing (and it seems to be the case since you say: "on every page change"), you can leverage several things:

  • Create a custom router-outlet (a sub class of RouterOutlet) that checks authentication with its activate method is called. In this case, you can have something global. Something like that:

    @Directive({
      selector: 'auth-outlet'
    })
    export class AuthOutlet extends RouterOutlet {
      (...)
    
      activate(oldInstruction: ComponentInstruction) {
        var url = this.parentRouter.lastNavigationAttempt;
        if (isAuthenticated()) {
          return super.activate(oldInstruction);
        } else {
          (...)
        }
      }
    }
    

    See this question for more details:

    • Angular 2 Authentication with child routes
  • Leverage the CanActivate decorator to check is a component can be activated or not. In your case, you can execute authentication checking at this level.

  • Something could also be done at the RouterLink level to show / hide route links. In this case, you can apply roles on these links based on related route configuration and current user hints. See this question for more details:

    • Angular2. How to hide(no-render) the link in the menu after check access?

This can be also handled within an HTTP interceptor (a class that extends the Http one). In this case, when a request is executing, you can plug some authentication checks:

@Injectable()
export class CustomHttp extends Http {
  constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
    super(backend, defaultOptions);
  }

  request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
    console.log('request...');
    if (isAuthenticated()) {
      return super.request(url, options).catch(res => {
        // do something
      });        
    } else {
      // Redirect to login page
      // Or throw an exception: return Observable.throw(new Error(...));
    }
  }

  (...)
}

See this question for more details:

  • How to create interceptors in Angular2?

Tags:

Jwt

Angular