firebase-tools: Firestore emulator: PERMISSION_DENIED

[REQUIRED] Environment info

firebase-tools: 6.10.0

Platform: macOS

[REQUIRED] Test case

This will take a moment, brb

[REQUIRED] Steps to reproduce

  1. Setup a Java project with the Firestore gcloud SDK, or the Firebase Admin SDK. Either way, make sure you’re on the newest Firestore release. In Gradle, for us, that’s:
  compile group: 'com.google.cloud', name: 'google-cloud-firestore', version: '1.6.0'
  1. Install the latest gcloud SDK locally. Gcloud reports the following for gcloud --version:
Google Cloud SDK 248.0.0
alpha 2019.05.17
app-engine-go 
app-engine-java 1.9.74
app-engine-python 1.9.85
beta 2019.05.17
bq 2.0.43
cloud-build-local 
cloud-datastore-emulator 2.1.0
cloud-firestore-emulator 1.4.6
cloud_sql_proxy 
core 2019.05.24
docker-credential-gcr 
emulator-reverse-proxy 
gsutil 4.38
pubsub-emulator 2019.04.26
  1. Write up a rules file for local emulator access to Firestore. Try your hardest to make it fully open - i.e. allow all traffic for basic testing.
rules_version = '2';

service cloud.firestore {
  match /databases/{database} {
    allow read, write: if true;

    match /{document=**} {
      allow read, write: if true;
    }
  }
}
  1. Try to runlistDocuments() on a query, receive the following exception:
com.google.cloud.firestore.FirestoreException: io.grpc.StatusRuntimeException: PERMISSION_DENIED: Metadata operations require admin authentication.
	at com.google.cloud.firestore.FirestoreException.apiException(FirestoreException.java:79)
	at com.google.cloud.firestore.CollectionReference.listDocuments(CollectionReference.java:140)
       [... lots of user code...]
  1. Think to yourself, okay, I’ll play this dumb credentials game. So you load up your JSON service account key file, and inject the credentials into the Firestore adapter, like you do for production access to Firestore:
    public static @Provides FirestoreOptions getFirestoreOptions() {
      // [... lots of prep ...]
      if (emulator) {
        final CredentialsProvider adminCreds = FixedCredentialsProvider.create(googleCreds);
        opts.setCredentials(googleCreds);
        opts.setCredentialsProvider(credentialsProvider);
        return opts.build();
      }
      return opts.build();
    }
  1. Run it again. Observe the following exception, because the emulator isn’t running via TLS, we see that the request is rejected again, this time because you cannot use credentials over a plaintext connection:
Caused by: com.google.api.gax.rpc.UnauthenticatedException: io.grpc.StatusRuntimeException: UNAUTHENTICATED: Credentials require channel with PRIVACY_AND_INTEGRITY security level. Observed security level: NONE
	at com.google.api.gax.rpc.ApiExceptionFactory.createException(ApiExceptionFactory.java:73)

[REQUIRED] Expected behavior

It should be possible to run queries on the local emulator for Firestore.

[REQUIRED] Actual behavior

Because of this catch-22 error setup, I don’t believe there is a way to run queries on the local emulator for Firestore, at least not queries that involve “metadata.”

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 35 (13 by maintainers)

Most upvoted comments

It is indeed expected behavior. You will need to be authenticated as an admin. The upside is that it’s actually much simpler than authenticating against prod firestore: all you need to do is pass in an "Authorization: Bearer owner" header. The bearer "owner" is accepted by the emulator for all projects.

If you’re familiar with @google-cloud/firestore you should be able to use the Admin SDK like so:

const grpc = require("@grpc/grpc-js");
const Firestore = require("@google-cloud/firestore");
const firestore = new Firestore({
  projectId: 'firestore-emulator-sample',
  servicePath: 'localhost',
  port: 8080,
  sslCreds: grpc.credentials.createInsecure(),
  customHeaders: {
    "Authorization": "Bearer owner"
  }
});

@antoniooi there have been a few bugs in the Admin SDK which are now all fixed (remove your node_modules and package-lock.json and run npm install again).

If you still have this error, please file a new issue with the simplest possible steps to reproduce and I’ll dig into it.

@antoniooi looks like there are a few things that may be going wrong:

  1. databaseURL is a setting for Realtime Database, not Cloud Firestore. Pointing that at your Firestore emulator could cause unknown issues.

  2. You are using this method to connect to the emulator:

db.settings({
    host: "localhost:8080",
    ssl: false
});

That’s actually how you connect the web JavaScript SDK to the emulator. The best way to connect the Admin SDK to the emulator is by using an environment variable:

export FIRESTORE_EMULATOR_HOST=localhost:8080

If that is set in your environment, when you do admin.initializeApp() we will automatically pick it up and connect to the emulator.

If you’re still having issues after trying these fixes, please open a new issue.

For anyone using ruby and arriving here via google, here’s a workaround for the google-cloud-firestore gem:

@firestore_client ||= Google::Cloud::Firestore.new(
  project_id: '...',
  # ...
)
if ENV['FIRESTORE_EMULATOR_HOST'].present?
  # https://github.com/firebase/firebase-tools/issues/1363#issuecomment-498364771
  @firestore_client.service.firestore.configure.metadata[:authorization] = 'Bearer owner'
end

@samtstern : Work like a charm with the following steps:

  1. Delete functions/node_modules and functions/package-lock.json.
  2. Run npm install at the functions directory.
  3. Run firebase emulators:start.
  4. Run set FIRESTORE_EMULATOR_HOST=localhost:8080.
  5. Run node import-test-data.js --> Test data go into the Firestore Emulator! 👍
  6. Change the firestore.rules to allow read, write: if false;.
  7. Run node import-test-data.js again --> No more PERMISSION_DENIED error. Test data updated at the Firestore Emulator accordingly! 👍

Everything works perfectly! All problems solved! Well done! 👍

I’ve probably faced the same problem, as the Admin SDK seems to be affected by the rules.

I tried emulators-codelab (https://github.com/firebase/emulators-codelab/tree/master/codelab-final-state) but functions emulator could not work properly.

Cart could not be recalculated. Error: 7 PERMISSION_DENIED: error log appeared from this code.

https://github.com/firebase/emulators-codelab/blob/b6c759ab8c6ba3f9f955cb248ee906021e09cf5f/codelab-final-state/functions/index.js#L28

Then I commented out all existing rules in firestore.rules and rewrote them to allow read, write;, emulator worked correctly.

Using firebase-tools version is here.

$ npx firebase --version
8.4.1

@samtstern : I think it is not the Admin SDK problem. It could be the Firestore emulator that is still bothering too much about the security rules and forgotten about Admin SDK privilege. If I go directly to the production server without running the emulators, the Admin SDK works as it should be – even with the rule allow read, write: if false;. I believe it is the emulator that is blocking it based on the rules.

@samtstern : Thanks for enlightening me. I didn’t know that databaseURL is only meant for Real-time Database. No wonder no matter what URL I put in, it still works, LOL. As for the db.settings, I tried before, without it, I can’t insert test data in my Firestore Emulator and make it appears in emulator UI – it will go straight to the Firestore production server despite Firestore emulator is properly running.

Honestly speaking, I’m just a beginner. Hope you don’t mind if I seek help from you further, where should I put this line of code:

export FIRESTORE_EMULATOR_HOST=localhost:8080

Is the above code just a pseudo or a fully functional code? Sorry for asking this because many experts here like to give only a “concept”, and made me foolishly hit the wall all the time. @_@

As what I know, when I start the emulator, the process.env shows that this has already been set for me, why should I set it again in my code? Thanks in advance!

Got it working, a bit hackily: https://gist.github.com/ryanpbrewster/aef2a5c411a074819c8d7b67be80621c

LMK if that’s enough to get you going

It also wouldn’t be too hard to implement this as a CredentialProvider in gax or what not. That would be super helpful, if you could point me in the right direction I’d be happy to file a PR or an issue in the right repo (because that’s way closer to general Google APIs Java than it is Firebase, if I’m not mistaken).

Thank you btw for the quick reply Firebase team 😃

edit: @ryanpbrewster feel free to close as wontfix once you reply.

The REST API looks like

curl "localhost:8080/v1/projects/example-project-name/databases/(default)/documents:listCollectionIds" \
  -X POST \
  -H 'Authorization: Bearer owner'

Out of curiosity, which SDK are you using here? Based on the stack trace it looks like Java?