I am trying to implement an auth guard in an Angular app which uses Firebase version 9 as backend. My goal is to set canActivate true when user has a document in Firestore, false otherwise. However I am having difficulties caused by asynchronous data when I try to get current authenticated user and user's documents from Firebase. Auth guard does not wait for User service to fetch current user and it's data. It returns false before User service even gets the current user in it's constructor. I have tried using Promises but not succeeded, I have checked other questions and tried to use Observables but was not able to apply them to my case. Here is my minimal code:
User service:
export class UserService {
firebaseApp: any;
db: FirebaseFirestore;
auth: Auth;
user: any;
constructor() {
this.firebaseApp = initializeApp(environment.firebase);
this.db = getFirestore(this.firebaseApp);
this.auth = getAuth(this.firebaseApp);
onAuthStateChanged(this.auth, user => {
this.user = user;
})
}
async isNewUser(uid: string): Promise<boolean>{
return new Promise<boolean>( async resolve => {
const docRef = doc(this.db, "users", uid);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
console.log("Document data:", docSnap.data());
resolve(false);
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
resolve(true);
}
})
}
isLoggedIn(): Promise<boolean> {
return new Promise<boolean>(async resolve => {
if(this.user == null){
resolve(false);
}else{
await this.isNewUser(this.user.uid).then(res => {
resolve(res);
})
}
})
}
}
Auth guard:
constructor(private userService: UserService){}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Promise<boolean> {
return new Promise<boolean>(resolve => {
this.userService.isLoggedIn().then(res=> {
resolve(res);
});
});
}
Also, what is the best practice here? How can I guarantee that user is not null, or data is fetched before calling any other function?