Angular 2+ guard has strange behavior on page refresh

1.6k views Asked by At

Using Asp Core on Angular 4 with the default template from asp core. The guard works, but on page refresh I get unwanted behavior. When refreshing a guarded route it briefly shows my login page when canActivate is true. Image below shows this behavior. Notice on refresh the screen flashes red (my login page).

enter image description here

Steps to reproduce issue:

  1. Create project with dotnet new angular
  2. Run the dotnet restore and npm install
  3. Add file auth.guard.ts (code below)
  4. Add file auth.service.ts (code below)
  5. Add login component
  6. Add service and guard to routes in app.modal.shared.ts (code below)
  7. Add login button on home component
  8. Run program and click login button
  9. Navigate to the counter route
  10. Press F5 to refresh, the login page will appear before showing counter route (should not show login page as canActivate should be true)

Please let me know if you wish to see any additional code or if you have any questions. I have been pulling my hair out the past two days trying all kinds of things with Observables, maps and subscriptions with no results. Any help will be greatly appreciated. Thanks in advance!

auth.guard.ts

import { Injectable } from '@angular/core';
import { Router, CanActivate } from '@angular/router'
import { AuthService } from './auth.service';
@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private authService: AuthService,
    private router: Router) {
  }
  canActivate() {
    if (!this.authService.isLoggedIn()) {
      this.router.navigate(['/login']);
      return false;
    }
    return true;
  }
}

auth.service.ts

import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
import { Router } from '@angular/router';
import { Response, Headers, RequestOptions } from '@angular/http';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
import { Observable } from 'rxjs/Rx';
import { BehaviorSubject } from 'rxjs/Rx';
@Injectable()
export class AuthService {
  private baseUrl: string = '';
  private loggedIn = false;
  uid: string | null;
  constructor(
    private router: Router,
    @Inject(PLATFORM_ID) private platformId: Object
  ) {
    if (isPlatformBrowser(this.platformId)) {
      this.loggedIn = !!localStorage.getItem('auth_token');
    }
  }
  login() {
    this.loggedIn = true;
    localStorage.setItem('auth_token', 'test');
  }
  logout() {
    localStorage.removeItem('auth_token');
    localStorage.removeItem('uid');
    window.location.replace('/home'); // redirect as we want all var and obj to reset
  }
  isLoggedIn() {
    return this.loggedIn;
  }
}

app.module.shared.ts

...
RouterModule.forRoot([
    { path: '', redirectTo: 'home', pathMatch: 'full' },
    { path: 'home', component: HomeComponent },
    { path: 'login', component: LoginComponent },
    { path: 'counter', component: CounterComponent, canActivate: [AuthGuard] },
    { path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthGuard] },
    { path: '**', redirectTo: 'home' }
])
...

EDIT: Added gif of issue.

EDIT: Found out this is an issue with server-side prerendering. I am currently looking into how to setup a token storage service and pass this to the server.

0

There are 0 answers