Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase insufficient permissions for .doc().get()

I am trying to authenticate a user in my app. I have a login screen that takes an email and password and then runs this:

login2(email: string, password: string): Observable<any> {
    const signInObs: Observable<UserCredential> = from(this.fAuth.signInWithEmailAndPassword(email, password));
    
    return signInObs.pipe(take(1), catchError(this.handleError), mergeMap((result: UserCredential) => {
        console.log(`done this ONE... ${result.user.uid}`);
        
        return this.firestore.collection('users').doc(result.user.uid).get().pipe(catchError(this.handleError), mergeMap((doc: DocumentSnapshot<any>) => {
            console.log(`done this two... ${doc.data().name}`);
            
            return result.user.getIdTokenResult(true).then((token: IdTokenResult) => {
                // authenticate the user in the code
                console.log(`done this three.. ${token.claims.admin}`);
                                    
                this.handleAuthentication(
                    result.user.email, 
                    result.user.uid,
                    doc.data().name,
                    token.claims.admin,
                    token.token 
                );
            }).catch(error => {
                throw new Error(error);
            });
        })); 
    }));
}

But the old ERROR FirebaseError: Missing or insufficient permissions. error happens with respect to the this.firebase.collection('users').doc(result.user.uid).get() part. This is all works just fine without that section - i.e. it logs in, gives me a token and so on. Everything works fine except it wont allow me to access that user record...

The rules in my database are:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    
    // match logged in user doc in users collection
    match /users/{userId} {
      allow create: if request.auth.uid != null;
      allow read: if request.auth != null && request.auth.uid == userId;
    }
    
    // match docs in the users collection
    match /users/{userId} {
        allow read, write: if request.auth.uid != null;
    }
  }
}

In reference to a reply that suggested it might be that FireAuth and Firestore modules aren't communicating correctly, this is how the app.module looks. These are all the references to the angular fire module in the app.module.

import { environment } from 'src/environments/environment';
import { AngularFireModule } from '@angular/fire';
import { AngularFireFunctions } from '@angular/fire/functions';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFirestoreModule } from '@angular/fire/firestore';

@NgModule({
  declarations: [
    // just components
  ],
  imports: [
    // other imports 
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFirestoreModule, 
    AngularFireAuthModule
  ],
  providers: [
        {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptorService, multi: true}, 
        AuthenticationService,
        AngularFireFunctions
    ],
  bootstrap: [AppComponent]
})
export class AppModule { }

And the constructor in the Authentication service:

constructor(private fAuth: AngularFireAuth, 
            private firestore: AngularFirestore,
            private router: Router) {
            }

I have tried running the id through the emulator and it seems like it should work. I have logged the userid from the login and it's identical to the document name it should be seeking.

This all seems like it should work but it doesn't. I have read a bunch of tutorials and none do anything hugely different - and differents are inconsistent between the tutorials.

I recognize the code isn't perfect in how it's written yet but wanted to make it functional first.

My data structure in the database looks like this:

Data structure

The ID of the documents is the user id of the relevant person, I double-checked they all are correct.

Double-checking it works otherwise if I allow universal read-write then everything works just fine... it's only when those rules come in that they don't work.

like image 889
Bogomip Avatar asked Mar 02 '23 13:03

Bogomip


1 Answers

You must specify which document you want to match, in this case it is all documents(recursively). Here is the rule:

  rules_version = '2';
  service cloud.firestore {
  match /databases/{database}/documents {
    
    // match logged in user doc in users collection
    match /users/{userId}/{documents=**} {
      allow create: if request.auth.uid != null;
      allow read: if request.auth.uid == userId;
    }
    
    // match docs in the users collection
    match /users/{userId}/{documents=**} {
        allow read, write: if request.auth.uid != null;
    }
  }
}

EDIT after comment: According to the https://firebase.google.com/docs/firestore/security/rules-structure#version-2 "In version 2 of the security rules, recursive wildcards match zero or more path items. match/cities/{city}/{document=**} matches documents in any subcollections as well as documents in the cities collection.". I guess that the code above solves the subcollection problem. If you still encounter problems, kindly share your data structure so that people can help better.

SECOND EDIT: I guess the problem occurs because request.auth is null. Firebase will throw error if there is null pointer.So before request.auth.uid, you must check if request.auth is null or not. Like that:

  rules_version = '2';
  service cloud.firestore {
  match /databases/{database}/documents {

    // match logged in user doc in users collection
    match /users/{userId}/{documents=**} {
      allow create: if request.auth != null && request.auth.uid != null;
      allow read: if request.auth != null && request.auth.uid == userId;
    }
    
    // match docs in the users collection
    match /users/{userId}/{documents=**} {
        allow read, write: if request.auth != null && request.auth.uid != null;
    }
  }
}

THIRD EDIT: I faced the same issue yesterday (rules are fine, and it works when all rules are set to true) but it was happening on mobile devices only, strangely. I found this issue on GitHub after searching: https://github.com/angular/angularfire/issues/2838. The problem was caused by the Firebase version. My version was 8.6.2 so I downgraded it to 8.6.1, and the problem was gone. You can downgrade it by:

npm install [email protected]

I think this is a very serious issue for Firebase, it holds people from developing and maintaining projects with Firebase backend. I hope this solves your issue too.

like image 125
AyseAsude Avatar answered May 16 '23 07:05

AyseAsude