mihirjoshi
mihirjoshi

Reputation: 12201

Firebase Permission denied when reading data after authentication

I am new to Firebase. I authenticated the user using email and password -

final Firebase ref = new Firebase("https://app.firebaseio.com");
ref.authWithPassword("[email protected]", "password", new Firebase.AuthResultHandler() {

    @Override
    public void onAuthenticated(AuthData authData) {
        System.out.println("User ID: " + authData.getUid());
        getData(ref);
    }

    @Override
    public void onAuthenticationError(FirebaseError firebaseError) {
    }
});

But after authentication when I am reading the data, I am getting Permission Denied.

private void getData(Query ref) {
    ref.addValueEventListener(new ValueEventListener() {

        @Override
        public void onDataChange(DataSnapshot snapshot) {
            System.out.println(snapshot.getValue());
        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {
            System.out.println("The read failed: " + firebaseError.getMessage());
        }
    });
}

These are the Firebase rules.

{
    "rules": {
        "users": {
            "$uid": {
                ".read": "auth !== null && auth.provider === 'password'"
            }
        }
    }
}

Upvotes: 4

Views: 9643

Answers (3)

Yaroslav Mytkalyk
Yaroslav Mytkalyk

Reputation: 17105

The problem is that Firebase has a bug, that after authentication, you have to wait until user appears in the FirebaseAuth, and then you can use it.

What I did is waited for it to appear, like

Observable.fromCallable(() -> signInImpl())
        .map(this::toFirebaseUser)
        .map(this::waitForUserToAppearInAuthenticator)
        .flatMap(this::doSomethingInDatabase);

Where

@NonNull
private FirebaseUser waitForUserToAppearInAuthenticator(@NonNull final FirebaseUser user) {
    final CountDownLatch cdl = new CountDownLatch(1);
    final FirebaseAuth.AuthStateListener l = firebaseAuth -> {
        final FirebaseUser cu = firebaseAuth.getCurrentUser();
        if (cu != null && user.getUid().equals(cu.getUid())) {
            cdl.countDown();
        }
    };
    mFirebaseAuth.addAuthStateListener(l);
    try {
        cdl.await(20, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    } finally {
        mFirebaseAuth.removeAuthStateListener(l);
    }

    return user;
}

Upvotes: 1

Ojonugwa Jude Ochalifu
Ojonugwa Jude Ochalifu

Reputation: 27237

I got this exception using a slightly different approach to read from my database, but this is how I solved the issue.

First of all, my database rules looked liked this:

{
"rules": {
  "student": {
    "$uid": {
      ".write": "auth != null && auth.uid == $uid",
      ".read": "auth != null && auth.uid == $uid"
      }
    }
  }
}

Earlier on, to write to to the student database, I used this code in my Activity:

 mAuthListener = new FirebaseAuth.AuthStateListener() {
        @Override
        public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
            user = firebaseAuth.getCurrentUser();
            if (user != null) {
                // User is signed in
                Log.e(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
            } else {
                // User is signed out
                Log.e(TAG, "onAuthStateChanged:signed_out");
            }
            // ...
        }
    };
    ...
    Student student = new Student();
    student.setPhoneNumber("+23480547455343");
    student.setCountryOfOrigin("Nigeria");
    mDatabaseReference.child("student").child(user.getUid()).setValue(student).
                addOnCompleteListener(DetailsCaptureActivity.this, 
                                   new OnCompleteListener<Void>() {
     ...
     });

Notice how the child name (student) matches the child name in the firebase data rules?

Now to read the data of this user, I did this:

 //Set up an AuthStateListener that responds to changes in the user's sign-in state:
    mAuthListener = new FirebaseAuth.AuthStateListener() {
        @Override
        public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
            user = firebaseAuth.getCurrentUser();
            if (user != null) {

                   databaseReference = firebaseDatabase.getReference().child("student").child(user.getUid());
                   databaseReference.addValueEventListener(new ValueEventListener() {
                      @Override
                      public void onDataChange(DataSnapshot dataSnapshot) {
                          Student student = dataSnapshot.getValue(Student.class);
                          phoneNumberTextView.setText(student.getPhoneNumber());
                      }

                      @Override
                      public void onCancelled(DatabaseError databaseError) {

                          Log.e(TAG, databaseError.getMessage());
                      }
                  });

            } else {
                Log.e(TAG, "onAuthStateChanged:signed_out");
            }
        }
    };

I got the permission denied exception if I did only:

databaseReference = firebaseDatabase.getReference().child(user.getUid());

Upvotes: 2

Frank van Puffelen
Frank van Puffelen

Reputation: 598668

Firebase's permission model only allows the user access to data that you explicitly give access to. Since in your security rules, you only give access to /users/$uid, the user cannot read from root /. The Firebase documentation covers this under "rules cascade".

You seem to want to use security rules to filter data, which is not possible with Firebase's security model. See the section "rules are not filters" in the Firebase documentation as well as these previous questions and answers:

The simplest solution is to allow read of the users node:

{
    "rules": {
        "users": {
            ".read": "auth !== null && auth.provider === 'password'"
        }
    }
}

And then query on that level:

getData(ref.child("users"));

Upvotes: 10

Related Questions