Angular 5 authentication app using angularfire2 and firebase. The app works fine navigating using in-app links e.g. redirect to dashboard after login or link to another page (co
The canActivate()
method is called directly on page refresh. So it always returns false
:
canActivate() {
this.authService.authState.subscribe(state => {
this.status = state.toString(); // This is called async/delayed.
});
// so method execution proceeds
// isLoggedIn() returns false since the login stuff in AuthService.constructor
// is also async: .subscribe((user) => { /* delayed login */ });
if(this.authService.isLoggedIn()) {
return true;
}
// so it comes here
this.router.navigate(['/']); // navigating to LoginComponent
return false; // and canActivate returns false
}
The solution:
import { CanActivate, Router, ActivatedRouteSnapshot,
RouterStateSnapshot } from '@angular/router';
// ...
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
// when the user is logged in and just navigated to another route...
if (this.authService.isLoggedIn) { return true; }
// proceeds if not loggedIn or F5/page refresh
// Store the attempted URL for redirecting later
this.authService.redirectUrl = state.url;
// go login page
this.router.navigate(['/']);
return false;
}
now, back in the little changed AuthService: (only have changed/relevant code here)
export class AuthService {
// new
redirectUrl: string;
// BehaviorSubjects have an initial value.
// isLoggedIn is property (not function) now:
isLoggedIn = new BehaviorSubject<boolean>(false);
// params declared private and public in constructor become properties of the class
constructor(private firebaseAuth: AngularFireAuth, private router: Router) {
// so this.user is not required since it is reference to this.firebaseAuth
this.firebaseAuth.authState.subscribe((user) => {
if (user) {
this.loggedIn.next(true);
// NOW, when the callback from firebase came, and user is logged in,
// we can navigate to the attempted URL (if exists)
if(this.redirectUrl) {
this.router.navigate([this.redirectUrl]);
}
} else {
this.loggedIn.next(false);
}
}
}
}
Note: I have written this code in the answer box and compiled it in my brain. So bugs may exist. Also I don't know if this is actually best practise. But the idea should be clear?!
Based on the Angular Routing Guide
Seems like there are similar problems/solutions out there: Angular 2 AuthGuard + Firebase Auth