I have a simple Ionic 4/5 tabbed application (I started from their tab starter). I would like to make one of the tabs guarded by authentication. Here's what I want to achieve:
- have one of the tabs reserved for logged in users, profile management, account management, etc..
- so when the user clicks the third tab, the default page is 'profile' which is guarded by an AuthGuard, if no user is present in ionic storage then redirect to the Login/Register page instead
Here's what I tried so far:
@Injectable({
providedIn: 'root'
})
export class AuthService {
static STORAGE_KEY = 'auth-user';
user: BehaviorSubject<User> = new BehaviorSubject(null);
redirectUrl: string;
constructor(
@Inject(ROUTES_CONFIG)
private readonly ROUTES: AppRoutes,
private readonly router: Router,
private readonly storage: Storage,
private readonly platform: Platform,
private readonly http: HttpClient
) {
this.platform.ready().then(() => {
this.checkUser();
});
}
async checkUser() {
const user = await this.storage.get(AuthService.STORAGE_KEY) as User;
if (user) {
this.user.next(user);
}
}
login(credentials): Observable<any> {
const loginObservable = this.http.post(`http://localhost:3000/auth/login`, credentials);
loginObservable.subscribe(async (user: User) => {
await this.storage.set(AuthService.STORAGE_KEY, user);
this.user.next(user);
this.router.navigateByUrl(this.redirectUrl || this.ROUTES.AUTH);
});
return loginObservable;
}
async logout(): Promise<void> {
await this.storage.remove(AuthService.STORAGE_KEY);
this.user.next(null);
this.router.navigateByUrl(this.ROUTES.LOGIN);
}
isLoggedIn(): Observable<User> {
return this.user;
}
}
And the guard:
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Promise<boolean> | Observable<boolean> | boolean {
let url: string = state.url;
// Store the attempted URL for redirecting
this.authService.redirectUrl = url;
return this.authService
.isLoggedIn()
.pipe(
skip(1),
map(user => {
if (user) {
console.log('authenticated');
return true;
}
console.error('Not authenticated, redirecting to login');
this.router.navigateByUrl(this.ROUTES.LOGIN);
return false;
})
)
}
The problem with this is once authenticated the authguard never goes into the map
. I put the skip(1)
there because I wanted to skip the initial null
value. How can I "defer" the AuthGuard until my AuthService checks if the user is present in the storage, because after all that's what I want to do.