0

Seems like a dumb question, but for some reason I can't figure out the answer.

I have an auth guard, which checks the expiration of the JWT token stored in local storage and throws "Your session has expired" dialog which redirects the user to login onClose.

In the current state, the dialog is thrown only if the browser has this token in the storage; otherwise it redirects to login page straight.

canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): boolean {
        if (!this._auth.isAuthenticated()) {
            if (this._auth.hasToken()) {
                this._dialogService.openErrorDialog('Your session has expired, please re-login', 0)
                    .afterClosed()
                    .subscribe(() => this.navigateToLogin(state.url));
            } else {
                this.navigateToLogin(state.url);
            }

            return false;
        }

    return true;
}

I want to implement the following logic: token expired while user browsed on some app page -> user navigated to another page -> dialog thrown -> redirect to login; but if user navigates to some page in the app from the outside, it should redirect him the login page straight, without throwing the dialog, even if there's already an expired token in the browser local storage.

I've tried to hack some solutions based on this, but it didn't work out - although I can return an Observable<boolean> from canActivate function, I couldn't figure out how can I make the rxjs map work with the results of pairwise - so I could use the fact of existence of the previous URL to determine where the user came from.

Any help greatly appreciated.

C.OG
  • 6,236
  • 3
  • 20
  • 38
grreeenn
  • 2,287
  • 1
  • 20
  • 27
  • user navigates to some page in the app from the outside - do you mean that you want to detect when a user followed a deep link to get to a page on your website? – Sergey Rudenko Nov 26 '19 at 17:07
  • 1
    "but if user navigates to some page in the app from the outside, it should redirect him the login page straight" - place that logic on `app.component` or elsewhere a user initializes once on entering your application. – Stavm Nov 26 '19 at 17:56

2 Answers2

2

Created stackblitz sample for you. Used the info you shared in the link and created a service like below.

@Injectable({ providedIn: "root" })
export class FirstVisitService {

  readonly isFirstVisit = new BehaviorSubject<boolean>(true);

  constructor(router: Router) {
    router.events
      .pipe(
        filter(event => event instanceof NavigationEnd),
        first()
      )
      .subscribe((e: NavigationEnd) => {
        this.isFirstVisit.next(false);
        this.isFirstVisit.complete();
      });
  }
}

and injected it in the auth.guard and used like this :

constructor(private service: FirstVisitService   ) { }

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | Promise<boolean> | boolean {

    if (this.service.isFirstVisit.value) {
      console.log("first time");
    }  else{
      console.log("not first time");
    }

    return true;
  }

and you have to import it in app module

export class AppModule {
  constructor(f : FirstVisitService) {}
}

So it takes place and subscribes the router event before any event happened.

Eldar
  • 9,781
  • 2
  • 10
  • 35
0

Just set a redirect in src/app/app.components.ts.
This "component" is the first one boostraped after the AppModule is boostraped.
So just drop the line there, and it will always redirect when user comes from the outside.


// src/app/app.components.ts

import { Component } from '@angular/core';
import { Router } from '@angular/router';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss'],
})
export class AppComponent {
    constructor(private router: Router) {
       // no matter where the user is going
       // just redirect to login!
       // this will only run the first time the app bootstraps
       this.router.navigateByUrl('/login'); // <--------
    }
}

guillefd
  • 1,903
  • 1
  • 16
  • 26
  • 1
    that's not what I want. If user does have a valid token, it shouldn't be redirected to login. Although I can inject the auth service to `AppComponent` and implement this logic, I like Eldar's solution more, as it doesn't mix between component and guard logic. Thank you anyway :) – grreeenn Nov 27 '19 at 10:39