Firestore onSnapshot rules throw error when deleting document - javascript

I'm having an issue with a Firestore observer and it's associated rules. I'm trying to have rules that only allow the user to see invites that contain the user's email address. However, if I delete a document from the collection, the snapshot observer throws an error Uncaught Error in onSnapshot: FirebaseError: Null value error. for 'get' # L92.
Here's my rules:
match /invites/{inviteID} {
function isSignedIn() {
return request.auth != null;
}
function isUserInvite() {
return request.auth.token.email != null &&
request.auth.token.email == resource.data.user_email;
}
allow read: if isSignedIn() && isUserInvite();
allow write: if true;
}
And here's my listener.
firebase.firestore()
.collection("invites")
.where(`user_email`, '==', myUserEmail) // this would be the users email
.onSnapshot((snapshot) => {
snapshot.forEach( doc => {
console.log(doc.id, doc.data())
})
})
It works for observing new documents, but fails when deleting a document. The following code makes the observer throw an error.
// creates a new invite and the snapshot listener informs me
await firebase.firestore()
.collection('invites')
.doc("invite_1")
.set({
user_email: someUserEmail, // use the users email here
date_added: firebase.firestore.FieldValue.serverTimestamp()
})
// deletes the invite, but the snapshot throws an error
await firebase.firestore()
.doc(`invites/invite_1`)
.delete()
UPDATE
I've found that this is only happening in the firestore emulator and not in production. Both the emulator and production have the same rules.

After submitting a bug report to Google - they confirmed it was a bug that only applied to the emulator. This was fixed in a new release. Here’s a link to the GitHub issue for any needed reference.
https://github.com/firebase/firebase-tools/issues/2197

Related

firebase applyActionCode always returns "auth/invalid-action-code"

Firebase returns invalid-action-code, when verifying an email.
Things to note: i made my own email logic/template; ie I manually generate the codes on firebase functions and dispatch them in the sent email.
The issue is, even though it returns "invalid-action-code", the user gets verified correctly. So it's a false positive. However I cannot differentiate from real-invalid-code cases.
const auth = getAuth();
let error = false;
await applyActionCode(auth, actionCode)
.catch(err => {
error = {
code: err.code,
message: err.message
}
})

Firebase Auth: can't delete Firestore documents in promise returned from currentUser.delete()

I am using firebase authentication and angularfirestore in my app, and I came across a strange problem. I want to delete a user document in firestore after I delete the user in firebase auth as shown below, but the firestore portion doesnt work.
Edit 2: In the code, this.currentUser is not the same as angularFireAuth.auth.currentUser. It is a local variable I created separately.
return this.angularFireAuth.auth.currentUser.delete().then(() => {
this.angularFirestore.collection("userData").doc(this.currentUser.email).delete().then(() => {
this.currentUser = null;
this.currentUserSubject$.next(this.currentUser);
this.setAuthenticationDetails({authType: null, rememberMe: false});
this.storageService.setRememberedUser(null);
});
})
However, if I were delete the document from firestore first, and delete the user from firebase auth afterwards, it works.
return this.angularFirestore.collection("userData").doc(this.currentUser.email).delete().then(() => {
return this.angularFireAuth.auth.currentUser.delete().then(() => {
this.currentUser = null;
this.currentUserSubject$.next(this.currentUser);
this.setAuthenticationDetails({authType: null, rememberMe: false});
this.storageService.setRememberedUser(null);
});
})
Anyone knows why this is happening? They both return promises so i expected the behaviour to be the same.
Edit 1: added catch, but nothing's being printed
return this.angularFireAuth.auth.currentUser.delete().then(() => {
return this.angularFirestore.collection("userData").doc(this.currentUser.email).delete().then(() => {
this.currentUser = null;
this.currentUserSubject$.next(this.currentUser);
this.setAuthenticationDetails({authType: null, rememberMe: false});
this.storageService.setRememberedUser(null);
}, (error) => {
console.log(error);
});
}, (error) => {
console.log(error);
})
Edit 3: Security Rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
What you're observing makes sense to me.
If you have security rules set up on Firestore that require an authenticated user in order to delete the documents, it makes sense that the documents would fail to delete after removing the user account (as the user is not longer authenticated). You will definitely want to do everything you can while authenticated before signing out or deleting the user account.
You should add error checking to your code to find out if anything went wrong with the document delete. Right now, you don't have any catch callbacks on the promise chain, so you'd never know if something went wrong. I suspect you'll see a "permission denied" type of error on the document delete due to security rules rejecting the request.
I think what happened is that after you do auth.currentUser.delete(), this.currentUser also gets cleared (if it holds auth.currentUser)
So you are just deleting doc("") which didn't fail. But there is no such document, so you won't see anything.

Can't login with Meteor function loginWithPassword, Unrecognized options for login request [400] error

I'm working on React + Meteor application and can't login using accounts-password package via loginWithPassword function.
The official API says that Unrecognized options for login request [400] error pops up when your user or password is undefined (or, i guess, just do not match the API), but i've checked the arguments and everything seems correct. username and password are strings. Meteor has ability to operate with user object, but this is not working too.
Here's the sample of my code.
const submit = useCallback(
(values) => {
const { email, password } = values;
const callback = (err) => {
if (err) {
console.log(err);
notifyError();
return;
}
login();
history.replace(from);
};
Meteor.loginWithPassword(email, password, callback);
},
[from, history, login, notifyError]
);
Any help appreciated.
It looks like that particular error can occur when there are no login handlers registered.
Have you added the accounts-password package ?
meteor add accounts-password

FirebaseError when logging out of web app, exception thrown by user callback, document references must have an even number of segments

I've built a Javascript web app using Firestore and Firebase. When logging the user out, I am getting console errors. The errors reference the firebase-database.js and firebase-firestore.js scripts, though, so I can't really tell what is happening:
[2020-05-22T12:32:58.436Z] #firebase/database: FIREBASE WARNING:
Exception was thrown by user callback.
Hr#https://www.gstatic.com/firebasejs/7.6.1/firebase-firestore.js:1:48219
firebase-database.js:1:11297
FirebaseError: Invalid document reference. Document references must
have an even number of segments, but user has 1
firebase-firestore.js:1:48219
This is my log out function:
$('.logout').on('click', function(){
firebase.auth().signOut()
.catch(function(error){
console.log(error.code);
console.log(error.message);
});
});
Then I have a listener for firebase.auth().onAuthStateChanged which triggers this:
firestoredb.collection('user').doc(uid).update({
status: false,
last_changed: firebase.firestore.FieldValue.serverTimestamp()
})
.then(function(){
uid='';
$('#screenname').html('');
window.location='https://www.example.com/your-account.asp?task=logout&afterlogin=%2Fv2';
})
.catch(function(error){
console.log(error.code);
console.log(error.message);
});
What might be my strategy for tracking down this error since the console logs are not that helpful? The error does not really affect the performance of the app, since the user is logged out anyway (and redirected via Javascript), however it bothers me that there is an error.
EDIT: I am wondering if the cloud script that is running could be the problem. That might explain why I cannot identify the line number and why the error message is so vague. Here is my cloud script, can this be modified so that a missing UID value would be ignored? This is basically the script provided by Google for combining Firebase and Firestore to maintain session state of the user.
const functions=require('firebase-functions');
const admin=require('firebase-admin');
admin.initializeApp();
const firestore=admin.firestore();
exports.onUserStatusChanged=functions.database.ref('user/{uid}').onUpdate(
async (change, context) => {
const eventStatus=change.after.val();
const userStatusFirestoreRef=firestore.doc(`user/${context.params.uid}`);
const statusSnapshot=await change.after.ref.once('value');
const status=statusSnapshot.val();
if (status.last_changed>eventStatus.last_changed){
return null;
}
eventStatus.last_changed=new Date(eventStatus.last_changed);
return userStatusFirestoreRef.update(eventStatus);
}
);

Access denied [403] when updating user accounts client-side in Meteor

I'm reading through the docs for Meteor here and the useraccounts package here but can't find an answer. I've added the useraccounts package successfully and have created a few users, but now I want to add some data to the record in the collection for a given user.
For example, after account creation and login. I want the user to be able to add/edit some fields on their record (short biography, etc..), but I keep getting a 403 error whenever performing a Meteor.users.update(..).
My login config file can be found here.
The code that's causing an error:
Template.editProfile.events({
'submit form': function(e) {
e.preventDefault();
var profileInfo = {
displayName: $(e.target).find('[name=displayName]').val(),
tagLine: $(e.target).find('[name=tagLine]').val(),
aboutMe: $(e.target).find('[name=aboutMe]').val()
};
Meteor.users.update(
{ _id: Meteor.userId()},
{ $set: profileInfo},
function (err) {
if(err) {
console.log('there was an error submitting editProfile data');
console.log(err);
} else {
Router.go('profile');
}
}
);
}
});
Doing console logs show the Meteor.userId() coming back correctly so I'm not sure what the problem is. I'm assuming it's an issue with allow/deny but I don't even know where to begin to troubleshoot.
The exact error is:
error: 403
errorType: "Meteor.Error"
message: "Access denied [403]"
reason: "Access denied"
By removing the insecure package, client-side write access will be denied by default.
If you want to allow clients to write directly to a collection, you need to define rules.
For example:
Meteor.users.allow({
update: ownsDocument
});
ownsDocument = function (userId, doc) {
return doc && doc.userId === userId;
};
The ownsDocument() function checks if the userId specified owns the document. In addition to the update callback, you can set rules for insert and remove.
Read more about Meteor's collection.allow(options), access a demo app or clone the repository.

Categories

Resources