Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by VoidProbe800

Why does my PHP Firestore Client return a PERMISSION_DENIED error with service account credentials?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

I'm using Firestore with a PHP backend (CodeIgniter 4.5.5) to save user data, but I keep getting a permission error when trying to read a document. My Firestore security rules are set as follows:

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

I followed Google’s official documentation and my PHP code looks like this:

PHP
$firestore = new FirestoreClient([ 'projectId' => env('firebase.project.id'), 'keyfileName' => env('firebase.key.file') ]); $collection = $firestore->collection('users'); $document = $collection->document($uid); //this one triggers the permission error $secret_key = $document->snaphot->get('secret_key');

The error I receive is:

BASH
PERMISSION_DENIED: Missing or insufficient permissions.

To test further, I modified my rules to:

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

However, this made no difference. Note that Firebase Authentication is working fine—I can create users and log them in. Also, turning off the rules removes the error, but that’s obviously not a solution. I’d appreciate any help in resolving this issue.

0

Comments 0

Answers 3

Views 45

3 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

Below is one explanation many have encountered when using Firestore with a PHP backend:


The Issue

Your Firestore security rule is written to allow reads/writes only when “request.auth” is not null (or when “request.auth.uid” is set). This works well for client SDKs where the Firebase Authentication token is automatically attached to the request. However, when you use the PHP library with a service account (via keyfile), the authentication context provided by a Firebase user isn’t available—the call is made with service account credentials, and Firestore still applies your security rules. In other words, the call is being treated as if no Firebase user is signed in, so “request.auth” is null, which is why you see:

  PERMISSION_DENIED: Missing or insufficient permissions.


Why Your Change Didn’t Help

Changing your rule from checking request.auth != null to request.auth.uid != null makes no difference because in both cases the server call made via your service account lacks the Firebase user token. The keyfile credentials do not add a user UID to the request; they just authenticate the caller as a service account. Unlike the Firebase Admin SDK, which bypasses security rules in trusted environments, the code in your PHP library (google/cloud-firestore) still enforces the rules you set.


Options for a Resolution

You have two main choices:

  1. Utilize Trusted Environment (Bypass Security Rules):

    If you’re running in a trusted backend, you might consider using the Firebase Admin SDK. Admin SDK calls bypass the Firestore security rules entirely. Unfortunately, the official Firebase Admin SDK for PHP is not available, which is why some opt for alternative solutions.

  2. Adjust Your Security Rules for Service Account Requests:

    You could modify your rules to grant your server (service account) access. For example, if you’re comfortable that calls from your server are trusted, you might add a condition to allow access to requests issued with your service account. One way is to use a custom claim (such as an “admin” claim) in your authentication token. For example:

    FIREBASE
    rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // Allow access if the request is authenticated as a normal user, // or if the request comes with a valid custom claim (e.g., admin == true) match /{document=**} { allow read, write: if request.auth != null || (request.auth.token.admin == true); } } }

    To use this, you’d need to create a custom token with the admin claim on your service account side and then attach it to your Firestore requests. This approach can work if you encrypt and sign these tokens properly.

    Note: Be very cautious with expanding rules. If you allow too much with a custom claim, you could expose your data inadvertently.


Recommended Best Practices

  • On Trusted Backends: Use an SDK or mechanism that bypasses or is exempt from security rules. In Node.js or Java, the Admin SDK automatically bypasses rules. For PHP, you may consider using a trusted proxy service if available.

  • On Client-Side Calls: Use Firebase client libraries so that the authenticated user’s ID token is attached to each request, satisfying your rules.

  • Testing: Always test your rules using the Firestore emulator before deploying them.


Summary

Your error occurs because your PHP backend’s calls with a service account do not have a Firebase user ID. Therefore, the condition (request.auth != null or request.auth.uid != null) fails, and the rules block access. Depending on your use case, either adjust your security rules (with caution) to allow trusted service account calls or use an Admin SDK that bypasses these rules from your backend.

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by ZenithNomad377 2 months ago

0

I don't know if the typo is in your actual code, but I'm pretty sure that it should be $secret_key = $document->snapshot->get('secret_key');, not $document->snaphot.

No comments yet.

Answer by EclipseRover245 2 months ago

0

Finally here is the solution.

I used kreait/firebase-php implementation of firestore. However make sure you fulfill all requirements listed in google's doc to make it work.

Dependencies in composer.json:

JSON
"firebase/php-jwt": "^6.10", "google/cloud-firestore": "^1.47", "google/gax": "1.35.1", "google/protobuf": "^4.29", "grpc/grpc": "^1.57", "kreait/firebase-php": "^7.0"

Firestore:

PHP
use Google\Cloud\Firestore\DocumentSnapshot; use Google\Cloud\Firestore\FirestoreClient; use Kreait\Firebase\Contract\Auth; use Kreait\Firebase\Factory; ... public function __construct() { $this->factory = (new Factory()) ->withServiceAccount(env('firebase.key.file')) ->withFirestoreDatabase('(default)'); $this->auth = $this->factory->createAuth(); //this one gives the FirestoreClient $this->firestore = $this->factory->createFirestore()->database(); } ... public function getUserData(string $uid) : DocumentSnapshot { $collection = $this->firestore->collection('users'); $user = $collection->document($uid); return $user->snapshot(); } public function saveUserData(string $uid, array $data) : void { $collection = $this->firestore->collection('users'); $user = $collection->document($uid); $user->set($data); }

No comments yet.

Discussion

No comments yet.