Evan Salter Evan Salter - 2 months ago 112
TypeScript Question

Angular2 canActivate() calling async function

I am trying to use Angular2 router guards to restrict access to some pages in my app. I am using Firebase Authentication. In order to check if a user is logged in with Firebase, I have to call

.subscribe()
on the
FirebaseAuth
object with a callback. This is the code for the guard:

import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AngularFireAuth } from "angularfire2/angularfire2";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Rx";

@Injectable()
export class AuthGuard implements CanActivate {

constructor(private auth: AngularFireAuth, private router: Router) {}

canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>|boolean {
this.auth.subscribe((auth) => {
if (auth) {
console.log('authenticated');
return true;
}
console.log('not authenticated');
this.router.navigateByUrl('/login');
return false;
});
}
}


When a navigate to a page that has the guard on it, either
authenticated
, or
not authenticated
is printed to the console (after some delay waiting for the response from firebase). However, the navigation is never completed. Also, if I am not logged in I am redirected to the
/login
route. So, the issue I am having is
return true
doesn't display the requested page to the user. I'm assuming this is because I am using a callback, but I am unable to figure out how to do it otherwise. Any thoughts?

Answer

canActivate needs to return an Observable that completes:

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(private auth: AngularFireAuth, private router: Router) {}

    canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>|boolean {
        return this.auth.map((auth) => {
            if (auth) {
                console.log('authenticated');
                return true;
            }
            console.log('not authenticated');
            this.router.navigateByUrl('/login');
            return false;
        }).first(); // this might not be necessary - ensure `first` is imported if you use it
    }
}

There is a return missing and I use map() instead of subscribe() because subscribe() returns a Subscription not an Observable

Comments