I am using AngularFire2 Authentication. All is working fine. Now what I wanted to achieve is, check it user is already in the userlist, then just update lastLogin the next time the facebook login button is clicked.
Else create a new user.
This works fine. But I can't get to stop the updateLogin() / _addUser() function to stop after updating the database.
It just keeps going. (89...)auth.service.ts:98 successfully logged in!
Here is the updateLogin()
private _updateLogIn(thisurl, data) {
return thisurl.update({
lastLogIn: firebase.database.ServerValue.TIMESTAMP,
avatar: data.photoURL
}).then((success) => {
console.log("successfully logged in!");
}); ;
}
I call this with the authentication() function as follow:
login(provider: string) {
this.af.auth.login({
provider: this._getProvider(provider)
}).then(
(success) => {
this.authenticate(success);
})
return;
}
authenticate(user: any): any {
if(!user) {
return {};
} else {
let data = user.auth.providerData[0];
this.api_url = this.af.database.object(`${this.path}/${user.auth.uid}`);
this._isUsers().subscribe(value => {
var filtered = value.filter(function(item) {
return item.uid === user.auth.uid;
});
if(filtered.length > 0){this._updateLogIn(this.api_url, data); return; }
else{ this._addUser(this.api_url, user, data); return;
}
});
return;
}
}
_isUsers(){
return this.usersList.map(snapshot => {
return snapshot;
});
}
private _addUser(thisurl, user, data){
return thisurl.set({
name: data.displayName,
username: data.displayName.replace(/ /g,"."),
avatar: data.photoURL,
email: data.email,
provider: data.providerId,
uid: user.auth.uid,
lastLogIn: firebase.database.ServerValue.TIMESTAMP
}).then((success) => {
console.log("successfully signed up!");
return;
});
}
How I stop the function execution to just once?
This basically hacks my browser tab.
(89...)auth.service.ts:98 successfully logged in!
The problem is most like related to the observable returned by _isUsers(). Although it's not clear from the question's code, it seems likely that the this.usersList returned by that function is an AngularFire2 list of all users.
AngularFire2 list and object observables emit data whenever the database changes, so it's likely that your setting the user data in _updateLogIn results in another list of users being emitted, etc.
To solve the problem, you could use the first operator:
import 'rxjs/add/operator/first';
this._isUsers().first().subscribe(...)
However, it's not clear why you need to subscribe to that observable in the first place. You might want to consider refactoring the code in your question, as it seems overly complicated.
It's generally a good idea to avoid as many subscribe calls as possible. You might want to read RxJS: Don’t Unsubscribe.
Related
I have to write a firebase function that receives a JSON with a list of users and has to manage them with the following rules. For each user in the received list:
If the user is already registered (email/password) in firebase, I update it.
If the user is not registered yet, I create it
If a user is registered in firebase but it's not present in the received list, I disable it.
Now, I came up with the following solution: I iterate for each user in the received list. I call admin.auth().createUser() method so that if the user is not registered it will be created, otherwise the method throws an error and in the catch() block I call admin.auth().updateUser().
For the second part, I retrieve all the users registered with admin.auth().listUsers() and for each of them I check whether it's present in the received list: if don't so, I disable it.
For some reason, the correctness of this solution is uncertain: sometimes it doesn't work at all, other times when I call the function once it doesn't work but the second time a call the function it works, idk why is that.
This only happens when I send to the function a lot of users (about 400). If I send only few users it works fine.
Could anyone suggest to me maybe a better solution? Thanks a lot for your answer.
This is the function:
exports.addClients = functions.https.onRequest(async (req, res) => {
// fetch recevied list from payload
var receivedClients = req.body.clients;
// create or update user
receivedClients.forEach(client => {
admin.auth().createUser({
uid: client.id,
email: client.email,
emailVerified: true,
password: client.password,
})
.catch(err => {
// update user
admin.auth().updateUser(client.id, {
email: client.email
}).catch(err => {
// error updating user
log("Error updating user: " + err);
});
})
});
// disabling users not present in the received list
listUsers = await admin.auth().listUsers();
userRecords = listUsers.users;
userRecords.forEach(record => {
if (!receivedClients.some(client => client.id === record.uid)) {
// disable user
admin.auth().updateUser(record.uid, {
disabled: true
})
.catch(err => {
// error disabling user
log("Error disaling user: " + err);
});
}
});
// send response
res.sendStatus(200);
});
I am trying to detect the userstate. If the user is logged in I want to set the data "userstate" to true. I am using vuefire and firebase into my vue project. I tried the way shown below, but it does not work
data() {
return {
userstate:false
};
},
watch:{
userstate:{
firebase.auth().onAuthStateChanged(function(user){
if(user){
this.userstate= true;}
else{
this.userstate=false;
}
})}
In Firebase you can check whether the user is signed in or not by using a function provided by the firebase which is auth().currentUser
// user will return true which means user EXISTS!
let user = firebase.auth().currentUser;
if (user) {
this.userstate = true; // If it exists
} else {
this.userstate = false; // If it doesn't
}
There are cases when the above mentioned method returns null / undefined for the user. So this solution is for your existing solution. So in that case try modifying your existing function to this:
async function IsLoggedIn() {
try {
await new Promise((resolve, reject) =>
firbase.auth().onAuthStateChanged(
user => {
if (user) {
// Yes User is signed in.
resolve('User is there');
} else {
// No user is not signed in.
reject('There is no user');
}
},
// Prevent console errors
error => reject(error)
)
)
return true
} catch (error) {
return false
}
}
Also since you intend to watch for the auth state change you can simply register the listener right after you initialize Firebase, you do not necessarily have to insert it in a VueJS watch block, you can insert it in your main.js for example, and if you are using a store like VueX you can update the state in the store and pull that information from any component of the VueX application.
firebase.initializeApp(configOptions);
firebase.auth().onAuthStateChanged(user => {
if (user) {
this.userstate = true;
} else {
this.userstate = false;
}
});
I have an authentication function using firebase which is just the auth code from the firebase documentation
export const signIn = (email,password) => {
firebase.auth().signInWithEmailAndPassword(email, password).then(()=>{
alert('Sign in Successful!')
}).catch(function(error) {
alert(error.message)
});
}
I call it like this
signIn(mail, password)
When I call it in my code, It works perfectly and the proper alerts appear. However, I want to actually receive something from my authentication function, like True or False if the user successfully logged in or not. Is there a way for me to receive this value from my function or any workarounds?
//evaluates to True if logged in successfully and vice versa
let authState = signIn(this.mail, this.password)
There's a couple approaches you could take with this, the first that comes to mind is the following:
export const signIn = (email, password) => {
return firebase.auth().signInWithEmailAndPassword(email, password).then(userCredential => {
alert('Sign in Successful!');
return true;
}).catch(error => {
alert(error.message);
return false;
});
}
// ......
let authState = await signIn(this.mail, this.password);
In promises you're able to return values from the .then() or .catch() method and then use that resolved value further in your code.
If you want to know when a user is signed in, no matter how they were signed in, you should instead use an auth state observer to set up a callback that will be invoked whenever the user becomes signed in or out, as shown in the documentation:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// User is signed out.
}
});
So I have designed a basic Publisher-Subscriber model using rhea in JS that takes an API request for saving data in DB and then publishes it to a queue.
From there a subscriber(code added below) picks it up and tries to save it in a DB. Now my issue is that this DB instance goes through a lot of changes during development period and can result in errors during insert operations.
So now when the subscriber tries to push to this DB and it results in an error, the data is lost since it was dequeued. I'm a total novice in JS so is there a way to make sure that a message isn't dequeued unless we are sure that it is saved properly without having to publish it again on error?
The code for my subscriber:
const Receiver = require("rhea");
const config = {
PORT: 5672,
host: "localhost"
};
let receiveClient;
function connectReceiver() {
const receiverConnection = Receiver.connect(config);
const receiver = receiverConnection.open_receiver("send_message");
receiver.on("connection_open", function () {
console.log("Subscriber connected through AMQP");
});
receiver.on("error", function (err) {
console.log("Error with Subscriber:", err);
});
receiver.on("message", function (element) {
if (element.message.body === 'detach') {
element.receiver.detach();
}
else if (element.message.body === 'close') {
element.receiver.close();
}
else {
//save in DB
}
}
receiveClient = receiver;
return receiveClient;
}
You can use code like this to explicitly accept the message or release it back to the sender:
try {
save_in_db(event.message);
event.delivery.accept();
} catch {
event.delivery.release();
}
See the delivery docs for more info.
I am working with Firebase and I'm having some troubles. When creating a new user, I am able to store it in my database, but later, when accessing to another component it fails.
//Register a new user in our system
registerUserByEmail(email: string, pass: string) {
return this.afAuth.auth
.createUserWithEmailAndPassword(email, pass)
.then(res => {
this.email = res.user.email;
let user = {
email: this.email,
goal: "",
previousGoals: [],
progress: {
accomplishedToday: false,
completedGoals: 0,
daysInRow: 0,
unlockedBadges: []
}
};
// store in database
new Promise<any>((resolve, reject) => {
this.firestore
.collection("users")
.add(user)
.then(
res => {
console.log(res.id);
this.isAuthenticated = true;
this.router.navigate(["/dashboard"]);
},
err => reject(err)
);
});
});
}
I believe that this piece of code is basically registering as a user the email and storing it successfully into my database (checked it).
Nevertheless, when rendering home.component or /dashboard
home.component
ngOnInit() {
this.setAuthStatusListener();
this.getUser();
}
getUser() {
this.data.getUser().subscribe(user => {
this.user = user.payload.data();
});
}
data.service
getUser() {
return this.firestore
.collection("users")
.doc(this.currentUser.uid)
.snapshotChanges();
}
I get the following error
ERROR
TypeError: Cannot read property 'uid' of null
It looks like by the time you call getUser the user hasn't been authenticated yet.
The simple fix to get rid of the error is to check for this condition in your DataService's getUser:
getUser() {
if (this.currentUser) {
return this.firestore
.collection("users")
.doc(this.currentUser.uid)
.snapshotChanges();
}
}
Given this sequence of calls however, I think there may be a better way to handle your use-case:
ngOnInit() {
this.setAuthStatusListener();
this.getUser();
}
Since you're attaching an auth state listener, you probably want to only start watching the user's data in Firestore once the user has actually been authenticated.
Once simple way to do that is to call out to your DataService's getUser() method from within the auth state listener, once the user is authenticated. Something like this:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
this.getUser(); // call the DataService's getUser method
}
});