How to get currently logged in auth state/user from angularfire2 - firebase

I have an ionic2 app and am using Firebase and angularFire2. I'd like to get the current authentication state and current auth object/user from firebase using angularFire2.
Here's what's working so far - I can authenticate the user and subscribe to the FirebaseAuthState to get the facebook user object.
constructor(platform: Platform, private auth: FirebaseAuth) {
auth.subscribe((user: FirebaseAuthState) => {
if (user) {
// I could store user in localstorage, but I'd like to see an All Firebase solution
this.rootPage = TabsPage;
} else {
this.rootPage = LoginPage;
}
});
Now I can just set localstorage here and cache my user object to remember auth state. However, I am curious to see how I can use Firebase only without me implementing my own custom local storage key. I see that Firebase stores a localStorage key of it's own so knows that its logged in.
How can I get the auth object from code? Additionally, I tried the listed example in the AngularFire2 documentation to render the auth state in the template - but that gives me an error.
import {FirebaseAuth} from 'angularfire2';
#Component({
selector: 'auth-status',
template: `
<div *ng-if="auth | async">You are logged in</div>
<div *ng-if="!(auth | async)">Please log in</div>
`
})
class App {
constructor (#Inject(FirebaseAuth) public auth: FirebaseAuth) {}
}

Import: import { AngularFireAuth } from 'angularfire2/auth';
Inject: constructor(public afAuth: AngularFireAuth) { }
Check:
this.afAuth.authState.subscribe(res => {
if (res && res.uid) {
console.log('user is logged in');
} else {
console.log('user not logged in');
}
});

now in order to get the user info, you have to subscribe for getting the auth information. Ex
constructor(public af: AngularFire) {
this.af.auth.subscribe(auth => console.log(auth));// user info is inside auth object
}
auth object will be null if auth state does not exist

current authentication state is available from the injected FirebaseAuth. You can get the actual auth data auth.getAuth()
See: https://github.com/angular/angularfire2/blob/master/src/providers/auth.ts#L99

You can simply import AngularFireAuth and do this:
this.angularFireAuth.authState.subscribe(userResponse => {
if (userResponse) {
console.log('here is your user data');
localStorage.setItem('user', JSON.stringify(userResponse));
console.log(userResponse);
} else {
localStorage.setItem('user', null);
}
});
Here i am also using localStorage to save all data of my current user active on my app.

Related

Firebase Authentication JS does not populate `providerData`array

in a VueJS / QuasarJS application Im using firebase-js-sdk [1] together with firebaseui-web [2] to handle authentication.
After successful auth with any of the configured providers (e.g. password, google, apple, etc) I want to check which provider the user used. But immediately after successful authentication the user.providerData[] array that should contain the information is empty.
BUT if I reload my app the user.providerData[] array is suddenly populated correctly.
Iยดm checking for user data with something like this
import { getAuth } from "firebase/auth";
const auth = getAuth();
const user = auth.currentUser;
if (user) {
console.log(user.providerData)
}
After that the user object is fully populated (incl auth tokens, etc) but the user.providerData[] array is empty. Only after a page reload (CTRL-R) does the array get populated.
I searched both projects issues pages and documentation and didnt find anything that could explain this.
Im thankful for ANY idea where to look next!
EDIT
As suggested by #aside Im using onAuthStateChanged to check for updates of the user state.
onAuthStateChanged(
fbAuth,
(user) => {
if (user) {
console.log("onAuthStateChanged: user found");
console.log("onAuthStateChanged: user.providerData", user.providerData);
console.log("onAuthStateChanged: user", user);
} else {
console.log("onAuthStateChanged: no user found");
}
},
function (error) {
console.log("onAuthStateChanged:", error);
}
);
But even if I wait minutes after authentication is completed, still the user.providerData array is only populated after a page reload.
Here is a full demo: https://codesandbox.io/s/github/perelin/firebase-auth-providerdata-test
Thanks in advance :)
Im using
"firebase": "9.6.1",
"firebaseui": "6.0.0",
[1] https://github.com/firebase/firebase-js-sdk
[2] https://github.com/firebase/firebaseui-web
Your app should call getAuth().currentUser.reload() to refresh the local user data after login.
This could be done either in beforeRouteEnter() nav guard of the LoggedIn view:
// LoggedIn.vue
import { getAuth, signOut } from "firebase/auth";
export default {
async beforeRouteEnter(to, from, next) {
await getAuth().currentUser?.reload() ๐Ÿ‘ˆ
next()
},
}
demo 1
Or in the onAuthStateChanged callback:
// main.js
onAuthStateChanged(
fbAuth,
async (user) => {
await user?.reload() ๐Ÿ‘ˆ
},
)
demo 2
Your code is only running once instead of running every time the auth state is updated.
If you want to listen to any changes to the auth state, use a callback along with onAuthStateChanged as described here.
https://firebase.google.com/docs/auth/web/manage-users#get_the_currently_signed-in_user
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
// Check used provider here
const providerData = user.providerData;
// ...
} else {
// User is signed out
// ...
}
});
The reason checking/requesting the user object right after authentication does not work is that it might take firebase a second to update the providerData array. signInWithX might therefore return before the property is updated.

auth().currentUser.reload() returns undefined

I am trying to check firebase emailVerified without logging out the user (Angular 6 web app), but auth().currentUser.reload() returns "undefined"
Tried:
import { auth } from 'firebase/app';
auth().currentUser.reload().then((u) => {
console.log(u);
});
Tried:
import { AngularFireAuth } from '#angular/fire/auth';
this.afAuth.auth.currentUser.reload().then((u) => {
console.log(u);
});
These related issues DO NOT HELP.
Update the email verification status without reloading page
Firebase: Observe email verification status in real time
Auth.auth().currentUser?.reload() doesn't refresh currentUser.isEmailVerified
The firebase core doesn't include the auth module. You need to add the auth module:
import firebase from 'firebase/app';
import 'firebase/auth';
If no user is currently authenticated, currentUser will be null and will not have a reload() function. Trying to call it would give you and error.
The reload() function returns a promise so you can use .then, but the promise doesn't return the currentUser object. To view the currentUser after the reload, just refer to the currentUser...
if (firebase.auth().currentUser) {
firebase.auth().currentUser.reload().then(() => {
console.log(JSON.stringify(firebase.auth().currentUser));
});
} else {
console.log('No authenticated user');
}

Vue Firebase : currentUser is null

I'm new in Vue and Firebase. I'm using Firebase query to retrieve some data from Firebase database based on the current user email.
This is working
source:firebase.database().ref('service').orderByChild('userEmail').equalTo('shakil#gmail.com')
So in this case I'm using firebase.auth().currentUser.email for using current user email
But this is not working
source:firebase.database().ref('service').orderByChild('userEmail').equalTo(firebase.auth().currentUser.email)
It shows a error like this-> firebase___default.a.auth(...).currentUser is null
And here is how Im using it into vuefire
firebase: {
services: {
source: firebase.database().ref('service').orderByChild('userEmail').equalTo('marin#gmail.com'),
readyCallback () {
this.services.forEach(service => {
firebase.storage()
.ref(`service_images/${service.fileName}`)
.getDownloadURL()
.then(img => {
this.$set(this.serviceImages, service['.key'], img)
})
})
}
}
},
During firebase initialization firebase.auth().currentUser is null.
You should use
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
as firebase recommendation.

ionic2 - angularfire2 - firestore: Missing or insufficient permissions on logout

i'm stuck on this issue working on a ionic2 project with "firestore" service from firebase.
I have an osservable to get some data from firestore in a template using the async pipe.
Rule on this EndPoint give read and write access only to logged user.
When i sign-out i put a redirect to login page.
..and now come the issue..
when i land in the login page, after a few second, jump out the IonicErrorHandler notifying that i have insufficient permission.
so;
how i can tell to firestore osservable;
"hey dude, stop it, i call u later if someone log-in again"
(ill try an unsubscribe() befour the signOut but not work, and also
it does not from persistence)
Recapping:
when i logOut
this.afAuth.auth.signOut();
the error:
core.es5.js:1020 ERROR Error: Missing or insufficient permissions.
at new FirestoreError (error.js:164)
at JsonProtoSerializer.fromRpcStatus (serializer.js:126)
at JsonProtoSerializer.fromWatchChange (serializer.js:517)
at PersistentListenStream.onMessage (persistent_stream.js:334)
at persistent_stream.js:270
at persistent_stream.js:247
at async_queue.js:81
at t.invoke (polyfills.js:3)
at Object.onInvoke (core.es5.js:3890)
at t.invoke (polyfills.js:3)
(to be precise, i recive it 3 times. Exactly the number or of documents in the collection)
Service where i call the firestore endpoint:
export interface Attivita {
id: string;
committente: string;
durata: number;
nome: string;
progetto: string;
userId: string;
}
#Injectable()
export class FirebaseService {
attivitaCollectionRef: AngularFirestoreCollection<Attivita>;
attivita$: Observable<Attivita[]>;
constructor(private afs: AngularFirestore,
public afAuth: AngularFireAuth ) {
}
setOsservableAttivita(uId){
this.attivitaCollectionRef = this.afs.collection('attivita', ref => {
return ref.where("userId", "==", uId)
});
this.attivita$ = this.attivitaCollectionRef.snapshotChanges().map(actions => {
return actions.map(action => {
console.log(action)
const data = action.payload.doc.data() as Attivita;
const id = action.payload.doc.id;
return { id, ...data };
});
});
}
}
tks in advance to all help me to understand it
:)
I'd recommend watching the authState from Firebase and only taking from snapshotChanges while you're authenticated. The switchMap operator allows you to switch between observables based on conditions such as whether or not the user is authenticated. Here is an example of a possible solution.
// Assuming rxjs 5.5.0 with lettable operators
import { map } from 'rxjs/operators/map';
import { switchMap } from 'rxjs/operators/switchMap';
import { empty } from 'rxjs/observable/empty';
import { create } from 'rxjs/observable/create';
const actions$ = this.attivitaCollectionRef.snapshotChanges()
.map(actions => {
return actions.map(action => {
console.log(action)
const data = action.payload.doc.data() as Attivita;
const id = action.payload.doc.id;
return { id, ...data };
});
});
}),
this.attivita$ = create(
// Listen for changes in the authState
subscriber => this.afAuth.onAuthStateChanged(subscriber))
)
.pipe(
// Determine if the user is logged in
map(user => !!user),
// switchMap will unsubscribe from the previous observable
// so when isLoggedIn switches to false actions$ will be unsubscribed from
switchMap(isLoggedIn => isLoggedIn ? actions$ : empty()),
);
calling this after logout solved it for me:
afStore.firestore.disableNetwork();
Yes,
Normally this is solved by doing an unsubscribe of your subscription in the ngOnDestroy() of the component you are navigating away from.
That's the way I do it.
So for you, that would be:
ngOnDestroy() {
this.attivita$.unsubscribe();
}
However, it is very difficult to pinpoint which you should unsubscribe as the error does not give any indication on that.
I have added a question to the devs of angularFire on your issue:
https://github.com/angular/angularfire2/issues/1459.
It would be nice to have helped so that the exception points you in the right direction, for example, the path which was not unsubscribed or the last path segment.
Also, there are alternative methods for doing this listed in this post:
http://brianflove.com/2016/12/11/anguar-2-unsubscribe-observables/
Hope that helps.

Using Firebase user UID with vuefire manual binding

In a simple SPA with Vue and Firebase, there are two routes: Login and Chat.
Upon login, the user is redirected to the Chat route where Firebase database bindings are done manually using vuefire's $bindAsArray(), inside the created() life-cycle hook. This is because the bindings require the uid assigned by the Firebase authentication to be available.
This works fine, until the user refreshes the page. If auth().currentUser is used to get the uid, it returns null. If the auth().onAuthStateChanged() watcher is used, Vue attempts to render the component before the Firebase database bindings are done. What am I missing?
I come across this scenario, as workaround I use component wrapper that has UID as property, if UID is null show a waiting message/animation else show your original component.
My real scenario is a little more complex to post it here (firebase, routing, vuex) but basically that wrapper component should look similar to this
<template>
<component :is="CurrentComponent" />
</template>
<script>
import App from './App';
import WaitingAnimation from './WaitingAnimation';
export default {
data() {
return {
Uid: null,
}
},
computed: {
CurrentComponent() {
return this.Uid == null ? WaitingAnimation : App;
}
}
beforeMount() {
//While Firebase is initializing `Firebase.auth().currentUser` will be null
let currentUser = Firebase.auth().currentUser;
//Check currentUser in case you previously initialize Firebase somewhere else
if (currentUser) {
//if currentUser is ready we just finish here
this.Uid = currentUser.uid;
} else {
// if currentUser isn't ready we need to listen for changes
// onAuthStateChanged takes a functions as callback and also return a function
// to stop listening for changes
let authListenerUnsuscribe = Firebase.auth().onAuthStateChanged(user => {
//onAuthStateChanged can pass null when logout
if (user) {
this.Uid = user.uid;
authListenerUnsuscribe(); //Stop listening for changes
}
});
}
}
}
</script>

Resources