firebase-tools: 7.30
Platform: macOS 10.14.6
My goal is to get authenticated firestore emulator requests working locally in a browser. The specific use case is for local testing. To test this case I've cloned the firebase/quickstart-nodejs repo and added a simple mock auth token to the firestore-emulator/browser-quickstart
sample as described in https://github.com/firebase/firebase-tools/issues/1001#issuecomment-523319483.
See the webchannel-auth-issue branch for a minimal test case demonstrating the issue (changes are contained in https://github.com/jkeys089/quickstart-nodejs/commit/a1456011525b0d40e5af14b38ca26898c6199151).
Run the firestore-emulator/browser-quickstart sample as normal.
The sample project should work as normal (i.e. allow the user to enter text in the textarea, store it in the local firestore emulator, and then see the text successfully shown above the textarea).
When running the sample project we see multiple errors in the browser development console when loading the page (Uncaught Error in onSnapshot: FirebaseError:
) and when attempting to post text (Uncaught (in promise) FirebaseError: PERMISSION_DENIED:
).
I actually spent some time debugging this issue and I believe I've discovered the issue. Although the source for the firestore emulator is not available I did manage to create a patch which fixes this bug / allows authenticated requests to function as expected:
--- com/google/cloud/datastore/emulator/firestore/webchannel/FirestoreV1WebChannelAdapter.java 2019-08-28 00:42:06.000000000 -0400
+++ FirestoreV1WebChannelAdapter.java 2019-08-27 23:34:54.000000000 -0400
@@ -190,7 +190,17 @@
String url = channel.getHandshakeHeaders().getUrl();
QueryStringDecoder decoder = new QueryStringDecoder(url);
String db = (String)((List)Preconditions.checkNotNull((List)decoder.parameters().get("database"), "expected %s to have a 'database' query parameter", (Object)url)).get(0);
- Context.current().withValue(FirestoreEmulatorMetadataKeys.DATABASE_REF.contextKey(), db).run(() -> {
+ String auth = null;
+ if (decoder.parameters().get("$httpHeaders") != null) {
+ for (String rawHeader : decoder.parameters().get("$httpHeaders").get(0).split("\r\n")) {
+ if (rawHeader.startsWith("Authorization:")) {
+ auth = rawHeader.substring(14).trim();
+ break;
+ }
+ }
+ }
+ Context.current().withValue(FirestoreEmulatorMetadataKeys.AUTHORIZATION.contextKey(), auth).withValue(
+ FirestoreEmulatorMetadataKeys.DATABASE_REF.contextKey(), db).run(() -> {
Object handler;
if (url.startsWith("/google.firestore.v1.Firestore/Write/")) {
handler = new FirestoreV1WebChannelAdapter.FirestoreWriteHandler(this.router, channel);
@jkeys089 I award you 100 internet points for providing a patch to a non-open source emulator! Thank you for making this really clear.
@ryanpbrewster or @IanWyszynski what do you think?
Impressive work :)
For what it's worth, that's very similar to the code that was actually added to handle this case. If you upgrade to v1.8.1 of the Firestore emulator (which shipped with v7.3.0 of the firebase-tools
package) this should be fixed.
Please re-open this issue if you encounter any other problems, and thanks again for the report! :grinning:
Most helpful comment
@jkeys089 I award you 100 internet points for providing a patch to a non-open source emulator! Thank you for making this really clear.
@ryanpbrewster or @IanWyszynski what do you think?