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()
}
}
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)
}
}