0

I am trying to write code in Unity to read a document in Firestore. I have a login code in one class that uses Firebase's email and password authentication. In another class I utilize Firestore objects try to access the document on Firestore, but I get the error:

Failed to register task with System.AggregateException: One or more errors occurred. ---> Firebase.Firestore.FirestoreException: Missing or insufficient permissions.

Below is the rules for Firestore:

rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /{document=**} {
          allow read: if request.auth != null;
        }
      }
    }

This is a snippet code for the login class:

private IEnumerator CheckDependencies()
    {
        var checkAndFixDependenciesTask = FirebaseApp.CheckAndFixDependenciesAsync();
        yield return new WaitUntil(predicate: () => checkAndFixDependenciesTask.IsCompleted);

        var dependencyResult = checkAndFixDependenciesTask.Result;

        if (dependencyResult == DependencyStatus.Available)
        {
            InitializeFirebase();
        }
        else
        {
            Debug.Log($"Error. Details: {dependencyResult}");
        }
    }
    public void InitializeFirebase()
    {
        auth = FirebaseAuth.DefaultInstance;
        auth.StateChanged += Auth_StateChanged;
        Auth_StateChanged(this, null);
        Debug.Log("Firebase Initialized");
    }

    private void Auth_StateChanged(object sender, System.EventArgs e)
    {
        if (auth.CurrentUser != user)
        {
            bool signedIn = user != auth.CurrentUser && auth.CurrentUser != null;

            if (!signedIn && user != null)
            {
                Debug.Log("Signed Out");
            }

            user = auth.CurrentUser;

            if (signedIn)
            {
                Debug.Log($"Signed In: {user.Email}");
            }
        }
    }

public IEnumerator RegisterEmailAndPassword(string email, string password, string username)
    {
        //StartCoroutine(CheckDependencies());
        var registerTask = auth.CreateUserWithEmailAndPasswordAsync(email, password);
        yield return new WaitUntil(predicate: () => registerTask.IsCompleted);
        Debug.Log("Register called.");
        if (registerTask.Exception != null)
        {
            //If there are errors handle them
            Debug.LogWarning(message: $"Failed to register task with {registerTask.Exception}");
            FirebaseException firebaseException = registerTask.Exception.GetBaseException() as FirebaseException;
            AuthError errorCode = (AuthError)firebaseException.ErrorCode;
            Debug.Log($"Exception: {registerTask.Exception} | Error: {errorCode}");

            switch (errorCode)
            {
                case AuthError.MissingEmail:
                    errorMessage = "Missing Email";
                    break;
                case AuthError.MissingPassword:
                    errorMessage = "Missing Password";
                    break;
                case AuthError.WeakPassword:
                    errorMessage = "Weak Password";
                    break;
                case AuthError.EmailAlreadyInUse:
                    errorMessage = "Email Already In Use";
                    break;
            }
        }
        else
        {
            user = registerTask.Result;

            if (user != null)
            {
                UserProfile profile = new UserProfile { DisplayName = username };

                var profileTask = user.UpdateUserProfileAsync(profile);
                yield return new WaitUntil(predicate: () => profileTask.IsCompleted);

                if (profileTask.Exception != null)
                {
                    Debug.LogWarning(message: $"Failed to register task with {profileTask.Exception}");
                    FirebaseException firebaseException = profileTask.Exception.GetBaseException() as FirebaseException;
                    AuthError errorCode = (AuthError)firebaseException.ErrorCode;
                }
                else
                {
                    StartCoroutine(SendVerificationEmail());
                    Debug.Log("Verification Sent");
                }

            }
        }
    }

    public IEnumerator SignInEmailAndPassword(string email, string password)
    {
        //StartCoroutine(CheckDependencies());
        var loginTask = auth.SignInWithEmailAndPasswordAsync(email, password);
        yield return new WaitUntil(predicate: () => loginTask.IsCompleted);

        if (loginTask.Exception != null)
        {
            //If there are errors handle them
            Debug.LogWarning(message: $"Failed to register task with {loginTask.Exception}");
            FirebaseException firebaseException = loginTask.Exception.GetBaseException() as FirebaseException;
            AuthError error = (AuthError)firebaseException.ErrorCode;

            switch (error)
            {
                case AuthError.MissingEmail:
                    errorText.text = "Email is missing";
                    break;
                case AuthError.MissingPassword:
                    errorText.text = "Password is missing";
                    break;
                case AuthError.WrongPassword:
                    errorText.text = "Incorrect Password";
                    break;
                case AuthError.InvalidEmail:
                    errorText.text = "Email is invalid";
                    break;
                case AuthError.UserNotFound:
                    errorText.text = "Account does not exist.";
                    break;
                default:
                    errorText.text = "Unknown error";
                    break;
            }
            loginError.SetActive(true);
            usernameField.text = "";
            passwordField.text = "";
        }
        else
        {
            user = loginTask.Result;

            if (!user.IsEmailVerified)
            {
                //Tell user to verify email and sign user out
                //Debug.LogFormat("User: {0} Email: {1} Not verified!", user.DisplayName, user.Email);
                StartCoroutine(SendVerificationEmail());
                errorText.text = $"User: {user.DisplayName} Email: {user.Email} Not verified!";
                
                auth.SignOut();
            }
            else
            {
                //Debug.LogFormat("User signed in: {0} ({1})", user.DisplayName, user.Email);
                //display main page
            }
        }
    }

    public IEnumerator SendVerificationEmail()
    {
        var verificationTask = user.SendEmailVerificationAsync();
        yield return new WaitUntil(predicate: () => verificationTask.IsCompleted);

        if (verificationTask.Exception != null)
        {
            Debug.LogWarning(message: $"Failed to register task with {verificationTask.Exception}");
            FirebaseException firebaseException = verificationTask.Exception.GetBaseException() as FirebaseException;
            AuthError error = (AuthError)firebaseException.ErrorCode;

            switch (error)
            {
                case AuthError.InvalidEmail:
                    errorMessage = "Email is invalid";
                    break;
                default:
                    errorMessage = "Unknown error: " + error;
                    break;
            }
        }
        else
        {
            Debug.Log("Verification Email Sent");
        }
    }

Below is the snippet to access Firestore document:

        public FirebaseFirestore firestore;
    
        public void Start(){
            indicator = FindObjectOfType<OfflineIndicator>();
            firestore = FirebaseFirestore.DefaultInstance;
            StartCoroutine(FetchRefreshToken());
        }
    
        public IEnumerator FetchRefreshToken()
        {
            DocumentReference db = firestore.Collection("school").Document("info");
            
            var task = db.GetSnapshotAsync();
            yield return new WaitUntil(predicate: () => task.IsCompleted);
    
            if (task.Exception != null)
            {
                Debug.Log($"Failed to register task with {task.Exception}");
                FirebaseException firebaseException = task.Exception.GetBaseException() as FirebaseException;
                FirestoreError error = (FirestoreError)firebaseException.ErrorCode;
    
                Debug.Log("Firestore Error Code: " + error);
            }
            else
            {
                DocumentSnapshot documentSnapshot = task.Result;
                Dictionary<string, object> docS = documentSnapshot.ToDictionary();
                //Debug.Log("Token " + docS["name"].ToString());
                name = docS["name"].ToString();
            }
        }

I create the necessary objects and call the required coroutines but I still get an error. It only works when I allow all access in Firestore rules.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
kbb
  • 3,193
  • 3
  • 18
  • 15
  • Can you try the security rules as in this [stackoverflow thread](https://stackoverflow.com/questions/46635830/missing-or-insufficient-permissions-when-writing-to-firestore-using-field-in-acc) – Priyashree Bhadra Mar 08 '22 at 10:27
  • Tried it but it did not work. – kbb Mar 09 '22 at 01:50
  • My guess is either you are not authenticated properly using email and password, or it is the firestore rules. Are you following this [documentation](https://firebase.google.com/docs/auth/unity/password-auth), can you check the [firestore rules](https://firebase.google.com/docs/database/security/rules-conditions) mentioned here? – Priyashree Bhadra Mar 09 '22 at 05:32
  • You may have a point. I moved the FetchRefreshToken() method into the login class and initialized the firestore object in the InitializeFirebase() method and it worked. So does that mean I need to authenticate every time I try to access firestore? If so, how do I do it? – kbb Mar 10 '22 at 05:54
  • I assume you are using Firebase Admin SDK. The Firebase Admin SDK has a built-in method for creating custom tokens.You need to provide a uid, which can be any string but should uniquely identify the user or device you are authenticating](https://firebase.google.com/docs/auth/admin/create-custom-tokens#create_custom_tokens_using_the_firebase_admin_sdk). These tokens expire after one hour. After you create a custom token, [you should send it to your client app](https://firebase.google.com/docs/auth/admin/create-custom-tokens#sign_in_using_custom_tokens_on_clients) – Priyashree Bhadra Mar 11 '22 at 08:52

0 Answers0