10

Im trying to do a variety of firebase actions in one call in a react-native app using react-native-firebase. the flow goes something like this:

  • create user in authentication
  • send image to storage
  • send data to firestore

During the image-storage phase, the imgRef.putFile() function errors out saying the user isn't authorized. However im using createUserWithEmailAndPassword() (which authenticates a user in on completion) and then using the returned credential to do the rest of the work, image storage and firestore creations.

firebase storage rules are set to allow only authenticated users. here's the ruleset:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

Also, I have enabled anonymous signin in authentication methods.

here's the initial action up until the error point:

return (dispatch) => {
        dispatch({ type: types.REGISTER_USER });
        console.log('starting registration process...');
        firebase
            .firestore()
            .collection('users')
            .where('username', '==', username)
            .get()
            .then((querySnapshot) => {
                console.log(querySnapshot);
                if (querySnapshot.empty !== true) {
                    registrationFail(dispatch, 'Username already taken. Try again.');
                    console.log("Registrant's username already exists");
                } else {
                    console.log('Registrants username is unique');
                    firebase
                        .auth()
                        .createUserWithEmailAndPassword(email, pass)
                        .then((userCredential) => {
                            uploadImg(dispatch, img, userCredential.user.uid)

here's the uploadImg() function:

const uploadImg = async (dispatch, uri, uid) => {
    console.log('Starting image upload...');
    dispatch({ type: types.UPLOAD_IMG, info: 'Uploading profile image...' });
    const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
    const imgRef = firebase.storage().ref('profile-images').child(uid);
    return imgRef
        .putFile(uploadUri, { contentType: 'image/png' })
        .then((success) => {
            console.log('profile image upload successful.')
            return imgRef;
        })
        .catch((err) => {
            console.log('profile image upload failed: ' + err)
            uploadImgFail(dispatch, err.message);
        });
};

again, the error that logs in the console is:

profile image upload failed: Error: User is not authorized to perform the desired action.

logging the firebase.auth().currentUser right before the uploading() function returns the current user object successfully.

this security rule issue also happens with Firestore, despite my security ruleset for the given collection being:

match /databases/{database}/documents {
    match /users/{uid} {
      // allow new user to check phone numbers
      allow read: if true
      allow update, create: if request.auth != null 
      allow delete: if request.auth.uid == uid
    }
 }

This is a part of my registration flow. I collect input, send relevant data to redux action, create a user, once the user is created, I add some data to a document in firestore. this makes no sense. im referencing the documentation and it still doesn't work.

How can this be fixed?

Jim
  • 1,988
  • 6
  • 34
  • 68
  • 1
    The error is suggesting that a user is not actually signed in at the time putFile is called. I suggest adding some logging to verify that a user is actually signed in at that time. – Doug Stevenson Aug 25 '20 at 21:43
  • @DougStevenson I logged the userCredential from `createUserWithEmailAndPassword()` and from that got the `lastSignInTime` which executed before the `.putFile()`. is this what you're referring to? – Jim Aug 26 '20 at 14:06
  • @DougStevenson I also logged `firebase.auth().currentUser` and it returned the same. any other ideas? – Jim Aug 26 '20 at 14:30
  • Did you remove security rules and check if image is uploading? Just to confirm security rules is not the reason. – Codex Sep 10 '20 at 11:35
  • @vinoth10 if i edit the create rule from `allow create: if request.auth != null` to `all create: if true` the registration goes through. but again like i mentioned, request.auth is not null at this point, ive logged the current user, and the `then(userCredential => {...})` returns the cred as expected – Jim Sep 10 '20 at 14:00
  • Shouldn't be `put` then 'putFile' (https://firebase.google.com/docs/storage/web/upload-files)? I tried `const storageRef = storage.ref().child(`avatars/${uid}`); storageRef.put(file, { contentType: file.type, }); storageRef.getDownloadURL().then((url: string) => { setImageUrl(url); });` after user creation with same rule and it worked for me – Codex Sep 14 '20 at 08:28
  • im using react-native-firebase @vinoth10 – Jim Sep 14 '20 at 12:37
  • Wheres @FrankVanPuffelen when you need him =( – Jim Sep 22 '20 at 00:57
  • 1
    Hey Jim. I checked this a while ago, but didn't see what might be going wrong. It might be worth seeing what happens if you add a short delay between the sign-in and the upload, just to see if there's a race condition in there. – Frank van Puffelen Sep 22 '20 at 01:05
  • I'm not familiar with Firebase however there seems to be some misunderstanding of terms here. You are talking about 2 different things here: authenticating and authorization. Authentication is the process of signing in. Authorization has to do with access rights. Let me take Facebook as example; I might signed into my account (thus I am authenticated), however I am not able to access the admin controls (my account is not authorized to use admin tools). (1/2) – 3limin4t0r Sep 25 '20 at 14:37
  • 1
    If what you are saying can be assumed as true and `createUserWithEmailAndPassword()` indeed authenticates a user on completion. It still might not grand the user permission for the action you are trying to do. (2/2) – 3limin4t0r Sep 25 '20 at 14:40
  • @FrankvanPuffelen thanks for chiming in frank, unfortunately even a 5 second delay did not change the outcome – Jim Sep 28 '20 at 23:32

1 Answers1

4

It seems to be an issue with firebase that you have to logout() the user once before you can log in after user creation. I faced the same issue and this is my workaround:

     firebase.auth().signInWithEmailAndPassword(userEmail,userPass).catch((error) => {
                /*console.log('Could not log in user');*/
                console.log(error);
                firebase.auth().createUserWithEmailAndPassword(userEmail,userPass).catch((error) => {
                    /*console.log('Could not create user');*/
                    console.log(error);
                }).then(result => {
                        firebase.auth().signOut().then(() => {
                            firebase.auth().signInWithEmailAndPassword(userEmail,userPass).then(result => {
                                /*console.log("here is you logged in user");*/
                            })
                        });
                    });
                });
            });
Lukas
  • 1,053
  • 8
  • 24
  • I'm running into this problem in a production app, and this solution is not acceptable. Something in firebase storage rules is broken. – Kevin Aug 26 '22 at 00:52