I'm using Firebase Authentication, email and password sign up.
My login function is working like a charm, I'm calling:
await setPersistence(this.auth, browserLocalPersistence);
after login, at it works. I'm saving the user data to firestore after creating a new user, that works too.
My problem is:
In my auth.service.ts
I'm trying to get the user document based on the auth.currentUser, I checked it in devtools, that the persistence is working properly, I get the currentUser from firebase always, when I need to.
So I'm doing this:
this.auth.onAuthStateChanged(user => {
console.log('FIRE AUTH ON AUTH STATE CHANGED RUNNING', user);
if (user) {
this.user$ = of(user).pipe(
switchMap(async user => {
try {
const res = await getDoc(doc(this.db, 'users', user.uid));
console.log(res.data());
return res.data() as FireUser;
} catch (error) {
console.error(error);
return null;
}
})
);
} else {
this.user$ = of(null);
}
});
Right after the login it works, but after I do refresh (and I still get the currentUser properly), the function just stops, without any errors, or anything. It just won't go inside the switchMap.
I'm logging the currentUser right in the start of onAuthStateChanged and I get it's value correctly..
(Also tried it like this):
onAuthStateChanged(this.auth, user => {
console.error('USER IS', user);
if (user) {
this.user$ = docData(
doc(this.db, 'users', user.uid)
) as Observable<FireUser>;
} else {
this.user$ = of(null);
}
});
Displaying the user$ Observable data in a component like this:
TS:
user$ = this.fireAuthService.user$;
HTML:
<div *ngIf="user$ | async as user">
<pre>
{{ user | json }}
</pre>
</div>
What am I doing wrong?
So my solution is (based on @Werner7's answer):
private userSubject$ = new BehaviorSubject<FireUser>(null);
get user$(): Observable<FireUser> {
return this.userSubject$.asObservable();
}
onAuthStateChanged(this.auth, async user => {
console.error('USER IS', user);
if (user) {
this.userSubject$.next(
(await getDoc(doc(this.db, 'users', user.uid))).data() as FireUser
);
} else {
this.userSubject$.next(null);
}
});
CodePudding user response:
This is what I do using behaviorSubject which I like because it's a little more dynamic.
export class AuthService {
private readonly user$ = new BehaviorSubject<UserModel | null>(null);
private _internalUserTmp: UserModel;
public get internalUserTemp(): UserModel {
return this._internalUserTmp;
}
private set internalUserTemp(value: UserModel) {
this._internalUserTmp = value;
}
get currentUser(): Observable<UserModel | null> {
return this.user$.asObservable();
}
constructor(
private readonly auth: Auth,
private readonly firestore: Firestore,
private readonly functions: Functions) {
onAuthStateChanged(this.auth, user => {
// console.log("State Chaned: ", user);
this.retrieveUserData(user);
auth.currentUser?.getIdTokenResult().then(idTokenResult => {
// console.log(idTokenResult);
if (!!idTokenResult.claims.superAdmin) {
this._isSuperAdmin$.next(true);
this._isAdmin$.next(true);
} else if(!!idTokenResult.claims.admin){
this._isAdmin$.next(true);
this._isSuperAdmin$.next(false);
}else {
this._isAdmin$.next(false);
this._isSuperAdmin$.next(false);
}
})
});
this.user$.subscribe(user => {
this.internalUserTemp = user as UserModel;
});
}
private async retrieveUserData(user: User | null): Promise<void> {
if (!user) {
this.user$.next(null);
return;
}
if (user.email === undefined || null || '') {
this.user$.next(null);
return;
}
await this.getUserDataByEmail(user.email ? user.email : '').subscribe(
userData => {
this.user$.next(userData);
}
);
}
getUserDataByEmail(email: string) {
return docData(doc(this.firestore, "users/", email));
}
Then I use UserModel which structured like this..
export class UserModel extends BaseModel {
uid?: string | null;
auth_id?: string | null;
adminReview?: boolean | null;
//...
constructor(config?: UserModel) {
super();
config = Object.assign({}, config);
this.uid = config.uid;
this.auth_id = config.auth_id;
this.adminReview = config.adminReview;
//...
}