I am trying to device my strategy for the best way to route through my app, knowing that a person needs to login in order to access a bulk of the app. Right now, my routes are setup like this:
const routes: Routes = [
{
path: '',
redirectTo: '/app/tabs/home',
pathMatch: 'full',
canActivate: [AuthGuard]
},
{
path: 'app',
loadChildren: () => import('./pages/tabs/tabs.module').then(m => m.TabsPageModule),
canActivate: [AuthGuard]
},
{
path: 'login',
loadChildren: () => import('./pages/login/login.module').then( m => m.LoginPageModule)
},
];
I am running into a problem that each time my app loads (and you have previously authenticated), there is a slight delay in the loading, which causing a white screen to appear, then my login screen appears, then you are routed to the correct destination. Currently, I am using an AuthGuard on the routes, and I use an Obersvable to see if a user has been authenticated, and if not, then redirect them to the login.
this.auth.isAuthenticated.subscribe((value) => {
if (!value) {
return this.router.navigate(['/login']);
}
});
I believe this may have something to do with some items being loading out of sequence.
My auth service is loaded in my app.component.ts as the platform loads:
this.platform.ready().then(() => {
this.statusBar.styleLightContent();
this.splashScreen.hide();
this.auth.load();
});
And in my auth.service.ts, this is the load function:
load() {
this.isAuthenticated.next(false);
this.getToken().then(token => {
if (token && token !== '') {
this.isAuthenticated.next(true);
} else {
this.router.navigateByUrl('/login');
}
});
Any thoughts on where I should be looking at what is causing the load sequence issue and why my login screen flashes up on each load of the app? }
EDIT
I believe the issue my be a symptom the isAuthenticated
BehaviorSubject in my Auth Service.
I changed my AuthGuard code to:
this.auth.isAuthenticated.subscribe((value) => {
if (!value) {
console.log("failed");
this.router.navigate(['/login']);
return false;
}
});
return true;
And each time the app reloads, I am seeing the failed
message in the console. Is something loading out of sequence with the Observable?
If your requirement is that you want to check the user authentication before hiding the splash screen, I suggest moving the splash screen hide code into your auth.load() method
this.platform.ready().then(() => {
this.statusBar.styleLightContent();
// this call will be moved into your auth
//this.splashScreen.hide();
this.auth.load();
});
// init with null
isAuthenticated = new BehaviorSubject<boolean>(null);
load() {
// don't set it to false..
// wherever you initialize it, set it to null for default value
// this.isAuthenticated.next(false);
this.getToken().then(token => {
if (token && token !== '') {
this.isAuthenticated.next(true);
} else {
this.isAuthenticated.next(false);
// this navigate probably isn't needed since you are doing it in your guards already, but I don't have all of your code to determine that
this.router.navigateByUrl('/login');
}
this.splashScreen.hide(); // you'll need to import it in your service for it to work
}).catch(error => {
// also, in case there is an error you probably also want to hide the splash screen
this.splashScreen.hide();
});
}
Also, you are setting isAuthenticated.next(false)
when you load so your route guard will always fail and redirect to the loading screen.
If isAuthenticated
is a BehaviorSubject you can initialize it to null
and in your route guard check if its null
and wait for an explicit true
or false
value before redirecting.
Updated Auth Guard
import { Router } from '@angular/router';
constructor(private auth: SomeAuthService, private router: Router) {}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.auth.isAuthenticated.pipe(
// skip initial "null" value, because that means we didn't finish our auth check
filter(val => val !== null)),
// now we will map the true/false response here
map(isAuthenticated => {
if (isAuthenticated) {
return true
} else {
// build a URLTree pointing to /login
// you need to import Router from angular
return this.router.parseUrl("/login")
}
})
}