As the title suggests, I want to protect routes via canLoad
. This works to some extent. However, I have two issues with the code.
Here are my code snippets
AppComponent
ngOnInit(): void {
this.authServ.reauthenticate().subscribe()
}
AuthService
user = new BehaviorSubject<User>(null as any);
reauthenticate(): Observable<User> {
// return HTTP call here and pipe the user data into user BehaviorSubject
}
AuthGuard
canLoad(){
return this.authServ.user.pipe(
map((user) => {
if (!!user?._token) {
return true;
}
this.router.navigate(['/login']);
return false;
})
);
}
As you can guess, the router can access the user but still redirects to the /login path. Knowing it doesn't work, I brute-forced my way, making two HTTP calls to the server (which is generally bad, I know) to trick the system into acknowledging a persistent user. Here is the code snippet.
AuthGuard
canLoad() {
return this.authServ.user.pipe(
switchMap((user) => {
if (!!user && !!user._token) {
return of(user);
}
return this.authServ.reauthenticate();
}),
map((user) => {
if (!!user?._token) {
return true;
}
this.router.navigate(['/login']);
return false;
})
);
}
So my question boils down to either of these two things:
I have checked the following links:
AuthGuard router angular
and What is the difference between Subject and BehaviorSubject?. However, upon using a Subject
, I may need to reauthenticate the user every time I visit a guarded route which is counterintuitive.
Okay, so I've solved this problem. What I did was to create a new Subject
at the AuthService. The guard checks if the BehaviorSubject
inside the AuthService returns a null, and if it does, I will switchMap
into using the Subject
. Here is the code.
AuthGuard
canLoad() {
return this.authServ.user.pipe(
switchMap((user) => {
if (!!user && !!user._token) {
return of(user);
}
return this.authServ.initialLogin;
}),
map((user) => {
if (!!user?._token) {
return true;
}
this.router.navigate(['/login']);
return false;
})
}
AuthService
user = new BehaviorSubject<User>(null as any);
initialLogin = new Subject<User>();
reauthenticate(): Observable<User> {
// return HTTP call here and pipe the user data into user BehaviorSubject
return this.http.get<User>(URL, {withCredentials: true}).pipe(tap((user) => {
this.initialLogin.next(user);
this.user.next(user);
}))
}
AppComponent
ngOnInit(): void {
this.authServ.reauthenticate().subscribe()
}
This way, I only authenticate once upon the start of the project