Home > Net >  Angular authentication and route guard
Angular authentication and route guard

Time:11-02

I'm building a angular spa front-end that consumes a GraphQL endpoint. After the users login, i set the token on localstorage and on my AuthService i set the auth state.My idea (coming from a React) was when the App component mounts ngOnInit i'll make a request for a me query who will return the user from the token stored on local storage, and i want to set the user on the AuthService. The problem i'm facing is that i've created a dashboard route which is protected, but the AuthGuard is not waiting for the App Component ngOnInit to finish and it will redirect to the login page.

import {Component, OnDestroy, OnInit} from '@angular/core';
import {MeGQL, User} from "../generated/graphql";
import {AuthService} from "./auth.service";
import {Router} from "@angular/router";

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html'
})
export class AppComponent implements OnInit {
  title = 'frontend';
  loading: boolean = true
  private meSubs: any;

  constructor(private meQuery: MeGQL, private authService: AuthService, private router: Router) {
  }

  async ngOnInit() {
    this.loading = true
    console.log("MONTOU APP")
    this.loading = true
    return this.meQuery.fetch({}, {
      fetchPolicy: "network-only",
    }).toPromise()
      .then(({data}) => {
        console.log("ENTROU NO THEN")
        if (data.me) {
          console.log(data.me)
          this.authService.setUser(data.me)
          this.loading = false
        }
      }).catch(e => {
        this.loading = false
        console.log("ERROR: ", e)
      })
  }


}
{{ loading }}
<div *ngIf="loading">Carregando...</div>
<div *ngIf="!loading">
    <router-outlet></router-outlet>
</div>
import { Injectable } from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from "@angular/router";
import {AuthService} from "../auth.service";
import {Observable} from "rxjs";

@Injectable({
  providedIn: 'root'
})
export class AuthGuardService implements CanActivate{

  constructor(private authService: AuthService, private router: Router) { }

  async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):  Promise<boolean >  {
    console.log("Auth Guard user mount")
    if(!this.authService.isAuthenticated()) {
      console.log("Não autenticado")
      await this.router.navigate(['/login'])
      return false
    }
    return true
  }
}
import {Injectable} from '@angular/core';
import {User, MeQuery, MeDocument, MeQueryVariables} from "../generated/graphql";
import {BehaviorSubject} from "rxjs";
import {Apollo} from "apollo-angular";

export type CurrentUserType = Pick<User, 'id' | 'name' | 'email' | 'active' | 'type'>

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private TOKEN_KEY = "AGENDEI_TOKEN"
  private currentUser: CurrentUserType | null = null
  private _isAuthenticated = new BehaviorSubject(false);
  private authSource = new BehaviorSubject<CurrentUserType | null>(null)


  constructor(private apollo: Apollo) { }

  loginUser(user: CurrentUserType, accessToken: string) {
    localStorage.setItem(this.TOKEN_KEY, accessToken)
    this.setUser(user)
    this._isAuthenticated.next(true)
  }

  setUser(user: CurrentUserType) {
    this.currentUser = user
  }

  async logout() {
    localStorage.removeItem(this.TOKEN_KEY)
    await this.apollo.getClient().resetStore()
    this._isAuthenticated.next(false);
  }

  public isAuthenticated(): Boolean {
    return this._isAuthenticated.value
  }

  public getUserFromMeQuery() {
    return this.apollo.query<MeQuery, MeQueryVariables>({
      query: MeDocument
    }).toPromise()
  }

}

enter image description here

CodePudding user response:

I believe making changes in canActivate method in your guard service will work.

Cause While checking if a user is logged in or not you are not waiting for auth service to set the Authentication state.

AuthService

 public isAuthenticated(): Promise<boolean> {
    return this._isAuthenticated.toPromise();
  }

AuthGuardService

async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):  Promise<boolean >  {
    console.log("Auth Guard user mount")
const isAuthenticated = await this.authService.isAuthenticated()
    if(!isAuthenticated) {
      console.log("Não autenticado")
      await this.router.navigate(['/login'])
      return false
    }
    return true
  }


CodePudding user response:

Try to use await in ngOninit of AppComponent:

async ngOnInit() {
    this.loading = true
    console.log("MONTOU APP")
    this.loading = true
    let response;
    try {
        response = await this.meQuery.fetch({}, {
                            fetchPolicy: "network-only",
                         }).toPromise()
        let {data} = response;
        if (data.me) {
           console.log(data.me)
           this.authService.setUser(data.me)
           this.loading = false
        }
    } catch (err) {
        this.loading = false
        console.log("ERROR: ", e)
    }
 }
  • Related