Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

request.auth is null in Firestore Rules when called from Firebase JS SDK in UWP #1491

Open
tomasszabo opened this issue Jan 22, 2019 · 49 comments
Open

Comments

@tomasszabo
Copy link

@tomasszabo tomasszabo commented Jan 22, 2019

I've discovered a problem when querying Firestore from UWP Universal Javascript project with Firebase JS SDK.

Problem description

Problem occurs when querying Firestore with authenticated user and Firestore Security Rules set to:

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

I'm always receiving:

FirebaseError: [code=permission-denied]: Missing or insufficient permissions.

With try and fail method I was able to find out that 'request.auth' object is 'null' in Security Rules because with following rule I'm able to successfully query Firestore:

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

However this Security Rule enables reading of data for everyone and stored data will not be secured.

In my opinion, the problem is related to the fact that access token is not processed (therefore request.auth is null). The only difference I've found is that HTTP header is NOT 'Origin: file://' (Origin header is completely missing) and CORS headers are not set correctly set in Response (CORS header are missing, but I'm not sure if this is the reason why access token is not processed).

Expected behavior

Auth token should be processed and Firestore Security Rules applied accordingly.

Attachements

UWP Project to reproduce the error (need some Firebase Test project and Firebase user):

https://1drv.ms/u/s!AjL4mBIpdvWDnYZqNkla5hHjjguGVg

HTTP Request from UWP:


   Request URL: https://firestore.googleapis.com/google.firestore.v1beta1.Firestore/Listen/channel?database=projects%2__project__%2Fdatabases%2F(default)&VER=8&RID=21133&CVER=22&X-HTTP-Session-Id=gsessionid&%24httpHeaders=Authorization%3ABearer%20__access-token__%0D%0AX-Goog-Api-Client%3Agl-js%2F%20fire%2F5.7.2%0D%0A&zx=7qsckzfmr536&t=1
   Request Method: POST
   Status Code: 200 / 
 
 - Request Headers
   Accept: */*
   Accept-Encoding: gzip, deflate, br
   Accept-Language: en-US, en; q=0.7, cs; q=0.3
   Cache-Control: no-cache
   Content-Length: 662
   Content-Type: application/x-www-form-urlencoded
   Host: firestore.googleapis.com
   User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; MSAppHost/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134
 
 - Response Headers
   alt-svc: quic=":443"; ma=2592000; v="44,43,39,35"
   cache-control: private
   content-encoding: gzip
   content-length: 71
   content-type: text/plain; charset=utf-8
   date: Tue, 01 Jan 2019 19:15:19 GMT
   server: ESF
   x-client-wire-protocol: h2
   x-content-type-options: nosniff
   x-frame-options: SAMEORIGIN
   x-http-session-id: GDPR57fJd-FfP6jaM98wCUpOO9SWVFty
   x-xss-protection: 1; mode=block

Javascript Console output:

Initializing firebase
main.js (49,5)
Signing in
main.js (55,5)
Signed in. Uid is G3nVlgyQIcWai7TN6nbBnmiJfBF2
main.js (69,9)
[2019-01-01T18:51:44.184Z]  @firebase/firestore: Firestore (5.7.2) [FirestoreClient]: Initializing. user= G3nVlgyQIcWai7TN6nbBnmiJfBF2
firebase-firestore.js (1,605)
[2019-01-01T18:51:44.186Z]  @firebase/firestore: Firestore (5.7.2) [MemoryPersistence]: Starting transaction: Get last stream token
firebase-firestore.js (1,605)
[2019-01-01T18:51:44.186Z]  @firebase/firestore: Firestore (5.7.2) [MemoryPersistence]: Starting transaction: Get next mutation batch
firebase-firestore.js (1,605)
[2019-01-01T18:51:44.188Z]  @firebase/firestore: Firestore (5.7.2) [MemoryPersistence]: Starting transaction: Allocate query
firebase-firestore.js (1,605)
[2019-01-01T18:51:44.188Z]  @firebase/firestore: Firestore (5.7.2) [MemoryPersistence]: Starting transaction: Execute query
firebase-firestore.js (1,605)
[2019-01-01T18:51:44.189Z]  @firebase/firestore: Firestore (5.7.2) [MemoryPersistence]: Starting transaction: Remote document keys
firebase-firestore.js (1,605)
[2019-01-01T18:51:44.189Z]  @firebase/firestore: Firestore (5.7.2) [Connection]: Creating WebChannel: https://firestore.googleapis.com/google.firestore.v1beta1.Firestore/Listen/channel [object Object]
firebase-firestore.js (1,605)
[2019-01-01T18:51:44.195Z]  @firebase/firestore: Firestore (5.7.2) [Connection]: Opening WebChannel transport.
firebase-firestore.js (1,605)
[2019-01-01T18:51:44.196Z]  @firebase/firestore: Firestore (5.7.2) [Connection]: WebChannel sending: {"database":"projects/xxx/databases/(default)","addTarget":{"query":{"structuredQuery":{"from":[{"collectionId":"settings"}],"where":{"fieldFilter":{"field":{"fieldPath":"createdBy"},"op":"EQUAL","value":{"stringValue":"G3nVlgyQIcWai7TN6nbBnmiJfBF2"}}},"orderBy":[{"field":{"fieldPath":"__name__"},"direction":"ASCENDING"}]},"parent":"projects/xxx/databases/(default)"},"targetId":2}}
firebase-firestore.js (1,605)
[2019-01-01T18:51:44.390Z]  @firebase/firestore: Firestore (5.7.2) [Connection]: WebChannel transport opened.
firebase-firestore.js (1,605)
[2019-01-01T18:51:44.496Z]  @firebase/firestore: Firestore (5.7.2) [Connection]: WebChannel received: {"targetChange":{"targetChangeType":"ADD","targetIds":[2]}}
firebase-firestore.js (1,605)
[2019-01-01T18:51:44.652Z]  @firebase/firestore: Firestore (5.7.2) [Connection]: WebChannel received: {"targetChange":{"targetChangeType":"REMOVE","targetIds":[2],"cause":{"code":7,"message":"Missing or insufficient permissions."}}}
firebase-firestore.js (1,605)
[2019-01-01T18:51:44.653Z]  @firebase/firestore: Firestore (5.7.2) [MemoryPersistence]: Starting transaction: Release query
firebase-firestore.js (1,605)
error on snapshot FirebaseError: [code=permission-denied]: Missing or insufficient permissions.
main.js (81,17)
   "error on snapshot"
   {
      [functions]: ,
      __proto__: { },
      code: "permission-denied",
      description: "Missing or insufficient permissions.",
      message: "Missing or insufficient permissions.",
      name: "FirebaseError",
      stack: "FirebaseError: [code=permission-denied]: Missing or insufficient permissions.
   at e (https://www.gstatic.com/firebasejs/5.7.2/firebase-firestore.js:1:71386)
   at t.prototype.fromRpcStatus (https://www.gstatic.com/firebasejs/5.7.2/firebase-firestore.js:1:138184)
   at t.prototype.fromWatchChange (https://www.gstatic.com/firebasejs/5.7.2/firebase-firestore.js:1:147575)
   at e.prototype.onMessage (https://www.gstatic.com/firebasejs/5.7.2/firebase-firestore.js:1:262030)
   at Anonymous function (https://www.gstatic.com/firebasejs/5.7.2/firebase-firestore.js:1:260812)
   at Anonymous function (https://www.gstatic.com/firebasejs/5.7.2/firebase-firestore.js:1:261598)
   at Anonymous function (https://www.gstatic.com/firebasejs/5.7.2/firebase-firestore.js:1:169060)"
   }
@google-oss-bot

This comment has been minimized.

Copy link

@google-oss-bot google-oss-bot commented Jan 22, 2019

I found a few problems with this issue:

  • I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.
  • This issue does not seem to follow the issue template. Make sure you provide all the required information.
@625dennis

This comment has been minimized.

Copy link

@625dennis 625dennis commented Jan 31, 2019

I'm having the same issue

@mikelehen mikelehen added the api: auth label Jan 31, 2019
@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Jan 31, 2019

@bojeil-google Do you know if there are any known issues with getting auth tokens in UWP (Universal Windows Platform) applications? I'm not sure if this is a problem on the auth side or the Firestore side. It looks like there's no Authorization header in the request, which suggests we got no token (or else it somehow got stripped from the XHR!?).

@bojeil-google

This comment has been minimized.

Copy link
Member

@bojeil-google bojeil-google commented Jan 31, 2019

I don't know much about UWPs and their intricacies. I remember there were some CORS issues in the past for file:// origins, but I thought they were resolved. I am not sure if this has to do with that.
It appears that the user is able to sign in (which means that the auth APIs and the calls to Auth server are working) from the console log, but somehow Firestore server is not able to get it.

@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Feb 1, 2019

@bojeil-google Thanks! Yeah, I don't know much either, unfortunately. Strangely this sounds similar to a report we got about an Electron app recently, but probably coincidence.

@625dennis @tomasszabo As a sanity check, could one of you add something like firebase.auth().currentUser.getIdToken().then(token => console.log('got token', token)) and verify you get a non-null token back?

@mikelehen mikelehen added the needs-info label Feb 1, 2019
@625dennis

This comment has been minimized.

Copy link

@625dennis 625dennis commented Feb 1, 2019

I'm developing with nuxt and I think the problem is with the nuxt server not having a local store. I will try to resolve it with cookie storage.

@tomasszabo

This comment has been minimized.

Copy link
Author

@tomasszabo tomasszabo commented Feb 1, 2019

@mikelehen Yes, the command you specified returns a non-null token. I've put it just before querying Firestore.

@bojeil-google UWP is not sending origin header at all (see "HTTP request from UWP" in my original post above). UWP is using ms-appx-web protocol, not file protocol, maybe that's the reason they're not filling origin header in requests.

@developius

This comment has been minimized.

Copy link

@developius developius commented May 14, 2019

I've got a feeling that this is a deeper issue with Firestore Security Rules.

When testing my rules in the simulator, everything is working fine until I run an unauthenticated test - I get a null reference error:

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      // allow anyone to read anything (we don't require auth in the app)
      allow read: if true
      // allow admins to do anything
      allow write: if isAdmin(request.auth.uid)
    }
    
    function isAdmin(uid) {
    	return isAuthenticated() &&
               userExists(uid) &&
               "admin" in getUser(request.auth.uid) &&
               getUser(uid).admin == true
    }
    
    function getUser(uid) {
    	return get(/databases/$(database)/documents/users/$(request.auth.uid)).data
    }
    
    function userExists(uid) {
    	return exists(/databases/$(database)/documents/users/$(request.auth.uid))
    }
    
    function isAuthenticated() {
    	return request.auth.uid != null; // null reference error
    }
  }
}

@tomasszabo, have you tried testing your rules in the simulator? What error do you get? You may need to check the raw response in dev tools to find it. For me, it doesn't seem to be able to resolve the request.auth object.

Update - solved
Just after posting this, I realised I might need to check if request.auth exists. This fixed it.

function isAuthenticated() {
    return request.auth != null && request.auth.uid != null;
}
@aaachris

This comment has been minimized.

Copy link

@aaachris aaachris commented May 29, 2019

I'm having the same problem with some Internet Explorer 11 users.
If I set security rules to read and write for authenticated user only in Firestore I'm getting:
FirebaseError: [code=permission-denied]: Missing or insufficient permissions.

Rules:
allow read, write: if request.auth != null && request.auth.uid != null;

If I remove these rules those Internet Explorer 11 user can access the data from Firestore.
So it seems like on some Internet Explorer 11 instances Firestore request aren't sent authenticated. On other Browsers the security rules work fine.

@thesandlord

This comment has been minimized.

Copy link

@thesandlord thesandlord commented Aug 27, 2019

I'm still seeing the same issue that @aaachris is with IE11, and I can't figure out if there are any IE11 security settings that are causing issues.

@eirmag

This comment has been minimized.

Copy link

@eirmag eirmag commented Aug 27, 2019

I do confirm having the exact same situation, and only in IE11. Firefox, chrome, edge are not affected by this situation. And the firebase.auth().currentUser.getIdToken().then(token => console.log('got token', token)) does send back a non-null token.

Nevertheless, as soon as we try to use onSnapshot, we get the

Uncaught Error in onSnapshot
code"permission-denied"
description"Missing or insufficient permissions."
message"Missing or insufficient permissions."
name"FirebaseError"

image

All of this with firebasejs 6.3.0

@CiriousJoker

This comment has been minimized.

Copy link

@CiriousJoker CiriousJoker commented Sep 10, 2019

Same problem here while using cordova. It works fine in the web version, but in the packaged app, request.auth seems to be null. The test from above is positive (ie I get a token back).

EDIT:
This problem seems to only exist in Chrome Dev 78. After changing the system Webview to the default one (stable 76), the problem was gone.

@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Sep 10, 2019

@CiriousJoker What version of Firebase are you using? There was an auth-related race condition that was fixed in the latest release (6.6.0). Just want to make sure that's not what you're running into. Thanks!

@CiriousJoker

This comment has been minimized.

Copy link

@CiriousJoker CiriousJoker commented Sep 10, 2019

I'm using 6.6.0, but this problem was present in 6.0.2 as well.

@thesandlord

This comment has been minimized.

Copy link

@thesandlord thesandlord commented Sep 12, 2019

@mikelehen 6.6.0 doesn't fix the problem in IE 11

@maxannear

This comment has been minimized.

Copy link

@maxannear maxannear commented Oct 15, 2019

We're having this issue in Electron 7 (Chromium 68). Electron 6 (Chromium 76) works correctly. We're using firebase 7.2.0 in both cases.

We're using a custom auth tokens

firebase.auth().currentUser.getIdToken().then(token => console.log('got token', token))
returns the correct token.

Yet any time we try access Firestore we get FirebaseError: Missing or insufficient permissions.

@klaussner

This comment has been minimized.

Copy link

@klaussner klaussner commented Dec 2, 2019

I ran into the same issue when I tried to use Firestore in a Firefox browser extension. Firestore requests fail with FirebaseError: "Missing or insufficient permissions." if the extension has the "<all_urls>" permission specified in its manifest, even though a user is signed in and is allowed to access all documents.

Here is a link to a minimal example repo: https://github.com/klaussner/firestore-firefox-extension-issue. The README.md lists the exact reproduction steps.

As has been discussed above, the problem seems to be how Firebase handles the HTTP Origin header. If a Firefox extension doesn't have the "<all_urls>" permission, the browser adds an Origin header (e.g., Origin: moz-extension://15a05323-f99f-3644-9304-508a5776b74d) to the XHRs sent to Firestore. However, if the extension has the "<all_urls>" permission, Firefox doesn't add this header (which, I think, is correct because the same-origin policy no longer applies).

It looks like the Firestore backend sets the auth object to null when it evaluates the Firestore rules if the request doesn't have an Origin header. Other Firebase services, including authentication and storage, work as expected.

@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Dec 2, 2019

@klaussner Thanks for the info. Would it be possible for you to share examples of the network requests being sent (both the broken one without Origin and a working one with Origin)? Please remove the actual contents of the auth token and anything else sensitive, or email me directly at michael@firebase.com.

@ketan1062

This comment has been minimized.

Copy link

@ketan1062 ketan1062 commented Dec 3, 2019

Hello @developius, Thanks for solutions. The solution saves my day.

@klaussner

This comment has been minimized.

Copy link

@klaussner klaussner commented Dec 3, 2019

@mikelehen Here are the first two requests sent to Firestore, once with and once without Origin headers. I hope this helps. 🤞 Please let me know if you need more information.

First Request

URL: https://firestore.googleapis.com/google.firestore.v1.Firestore/Listen/channel?database=[DATABASE]&VER=8&RID=95405&CVER=22&X-HTTP-Session-Id=gsessionid&%24httpHeaders=[TOKEN]&zx=o41gvibi0nw4&t=1

Request Headers

Host: firestore.googleapis.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: */*
Accept-Language: en,de;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 479
Origin: moz-extension://7eef82bd-5974-b84e-b4b5-15ea916f0bcc
Connection: keep-alive
TE: Trailers

Response Headers

HTTP/2.0 200 OK
x-client-wire-protocol: h2
x-http-session-id: -c1eQIx99Kaa-VdK3lFSkUlqLML2_CvK
content-type: text/plain; charset=utf-8
content-encoding: gzip
date: Tue, 03 Dec 2019 19:25:14 GMT
server: ESF
cache-control: private
content-length: 71
x-xss-protection: 0
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
access-control-allow-origin: moz-extension://7eef82bd-5974-b84e-b4b5-15ea916f0bcc
access-control-allow-credentials: true
access-control-expose-headers: x-client-wire-protocol,x-http-session-id
alt-svc: quic=":443"; ma=2592000; v="46,43",h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000
X-Firefox-Spdy: h2

Response Body

51
[[0,["c","2A3yo6U6BtLH5jlrG9Qfmg","",8,12,30000]]]

Second Request

URL: https://firestore.googleapis.com/google.firestore.v1.Firestore/Listen/channel?database=[DATABASE]&gsessionid=Zm4JCnVrJZ-x4Du7mQoVd-tkvZCNMyio&VER=8&RID=rpc&SID=2A3yo6U6BtLH5jlrG9Qfmg&CI=0&AID=0&TYPE=xmlhttp&zx=x7eby92w90u6&t=1

Request Headers

Host: firestore.googleapis.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: */*
Accept-Language: en,de;q=0.5
Accept-Encoding: gzip, deflate, br
Origin: moz-extension://7eef82bd-5974-b84e-b4b5-15ea916f0bcc
Connection: keep-alive
TE: Trailers

Response Headers

HTTP/2.0 200 OK
cache-control: private, max-age=0
x-content-type-options: nosniff
content-type: text/plain; charset=utf-8
content-encoding: gzip
date: Tue, 03 Dec 2019 19:25:14 GMT
server: ESF
x-xss-protection: 0
x-frame-options: SAMEORIGIN
access-control-allow-origin: moz-extension://7eef82bd-5974-b84e-b4b5-15ea916f0bcc
access-control-allow-credentials: true
access-control-expose-headers: x-client-wire-protocol,x-http-session-id
alt-svc: quic=":443"; ma=2592000; v="46,43",h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000
X-Firefox-Spdy: h2

Response Body

409
[[1,[{
  "targetChange": {
    "targetChangeType": "ADD",
    "targetIds": [
      2
    ]
  }
}
]],[2,[{
  "targetChange": {
    "targetChangeType": "CURRENT",
    "targetIds": [
      2
    ],
    "resumeToken": "CgkI9fXb+Zua5gI=",
    "readTime": "2019-12-03T19:31:47.191541Z"
  }
}
]],[3,[{
  "targetChange": {
    "resumeToken": "CgkI9fXb+Zua5gI=",
    "readTime": "2019-12-03T19:31:47.191541Z"
  }
}
]]]14
[[4,["noop"]]]14
[[5,["noop"]]]

⚡️

First Request

URL: https://firestore.googleapis.com/google.firestore.v1.Firestore/Listen/channel?database=[DATABASE]&VER=8&RID=68201&CVER=22&X-HTTP-Session-Id=gsessionid&%24httpHeaders=[TOKEN]&zx=mf0pmb6655q&t=1

Request Headers

Host: firestore.googleapis.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: */*
Accept-Language: en,de;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 479
Connection: keep-alive
TE: Trailers

Response Headers

HTTP/2.0 200 OK
x-client-wire-protocol: h2
x-http-session-id: _wRLMJcSuFdFDVoCx4jW6RaI4eWLcDmQ
content-type: text/plain; charset=utf-8
content-encoding: gzip
date: Tue, 03 Dec 2019 19:29:16 GMT
server: ESF
cache-control: private
content-length: 71
x-xss-protection: 0
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
alt-svc: quic=":443"; ma=2592000; v="46,43",h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000
X-Firefox-Spdy: h2

Response Body

51
[[0,["c","rILgOaemnhCrHGXkUgfTdg","",8,12,30000]]]

Second Request

URL: https://firestore.googleapis.com/google.firestore.v1.Firestore/Listen/channel?database=[DATABASE]&gsessionid=aX5IaaGrINK_oKA9GzeCENYYTUpPm4gI&VER=8&RID=rpc&SID=rILgOaemnhCrHGXkUgfTdg&CI=0&AID=0&TYPE=xmlhttp&zx=hfsvpk5t4xzf&t=1

Request Headers

Host: firestore.googleapis.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: */*
Accept-Language: en,de;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
TE: Trailers

Response Headers

HTTP/2.0 200 OK
cache-control: private, max-age=0
x-content-type-options: nosniff
content-type: text/plain; charset=utf-8
content-encoding: gzip
date: Tue, 03 Dec 2019 19:29:16 GMT
server: ESF
x-xss-protection: 0
x-frame-options: SAMEORIGIN
alt-svc: quic=":443"; ma=2592000; v="46,43",h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000
X-Firefox-Spdy: h2

Response Body

297
[[1,[{
  "targetChange": {
    "targetChangeType": "ADD",
    "targetIds": [
      2
    ]
  }
}
]],[2,[{
  "targetChange": {
    "targetChangeType": "REMOVE",
    "targetIds": [
      2
    ],
    "cause": {
      "code": 7,
      "message": "Missing or insufficient permissions."
    }
  }
}
]]]14
[[3,["noop"]]]14
[[4,["noop"]]]
@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Dec 3, 2019

@klaussner Thank you! I will try to pull in some backend folks who may have insight into what's going on.

@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Dec 4, 2019

@klaussner By the way, if you're willing to collect another data point, it would be interesting to know if transactions suffer from the same problem. Something along the lines of:

let db = firebase.firestore();
await db.runTransaction(transaction => {
  transaction.set(db.doc('example/foo'), {a: 1});
});

Transactions make direct REST requests instead of going through our WebChannel transport, so they may behave differently.

@klaussner

This comment has been minimized.

Copy link

@klaussner klaussner commented Dec 4, 2019

@mikelehen Transactions are working without an Origin header. 👍

@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Dec 17, 2019

@agray5 I don't know of a workaround without modifying the SDK. FWIW, just commenting out this line would likely be enough:

request.httpHeadersOverwriteParam = '$httpHeaders';

In fact, if somebody wants to do that experiment, it would be useful to verify the theory for sure.

@agray5

This comment has been minimized.

Copy link

@agray5 agray5 commented Dec 17, 2019

@mikelehen This is a pretty big holdup in my project, so I'll clone the repo and see if that suggestion works and report it back here.

@agray5

This comment has been minimized.

Copy link

@agray5 agray5 commented Dec 18, 2019

@mikelehen Just got done testing it out and all my data is back! That line seems very much to be the culprit, at least for Electron.

@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Dec 18, 2019

@agray5 Thanks! If you were interested in creating a PR, I think we need an isElectron() function similar to isReactNative() that checks for Electron, maybe doing something like getUA().indexOf(' electron/') > -1)

and then add that to the check at

@amammay

This comment has been minimized.

Copy link

@amammay amammay commented Dec 18, 2019

@mikelehen since the root cause of this issue genuinely seems like a back end issue, will there be a plan to remove these checks once the root cause if fixed? seems like we are just coding around a actual issue 😐

@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Dec 18, 2019

@amammay Yes, when the backend issue is resolved we can remove these checks, but that may not happen for some time unfortunately.

@tomasszabo

This comment has been minimized.

Copy link
Author

@tomasszabo tomasszabo commented Dec 18, 2019

@mikelehen Please don't forget that this is not only Electron issue, other environments like UWP, Firefox, etc. are also impacted.

@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Dec 18, 2019

@tomasszabo Yep, understood! Thanks for the reminder. 😁

For some context, the underlying backend issue has been around for over 2 years. So I'm working to get the backend team to fix this, but backend fixes are slow to roll out and this isn't a high-priority issue for them since it's been broken for so long.

So my suggestion for anybody that's blocked by this and would like a fix in the short-term is to help us add exclusions for affected environments to the SDK. That would get the issue fixed within 1-2 weeks (pending holiday release schedules). From the thread, the environments I've seen mentioned are: UWP apps, IE11, Firefox Extensions (with the "<all_urls>" permission specified in its manifest), and Electron 7.

Else you are also welcome to wait for a backend fix, but I have no timeline to offer on that. I'd expect it to be on the order of months.

@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Dec 18, 2019

I'm probably going to go ahead and put together a PR that checks for IE11 soon (today?). I can check for Electron too, assuming navigator.useragent.indexOf(' electron/') > -1) is sufficient. If anybody knows how to detect any of the other affected environments (Firefox extension, UWP apps) and wants to chime in, I may be able to include them in the PR as well.

@tomasszabo

This comment has been minimized.

Copy link
Author

@tomasszabo tomasszabo commented Dec 18, 2019

It looks like that User Agent for UWP on Windows 10 and Windows 10 Mobile (yeah, it still exists) contains MSAppHost/3.0 (not sure about that /3.0 if it's not incremented with new Windows versions), for Xbox it contains Xbox (yep, UWP Apps runs also on Xbox but have not tried that).

mikelehen added a commit that referenced this issue Dec 20, 2019
…ader.

There is a backend bug (b/145624756) causing $httpHeaders to be ignored if an Origin header is not sent.  Via #1491 we've found that several environments can run into this issue so until the backend issue is fixed we are excluding them from using $httpHeaders. This may incur an extra CORS preflight round-trip in some cases, but this is much better than having our Authorization header get ignored.
@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Dec 20, 2019

Thanks @tomasszabo and @agray5! I have a proposed PR that should work around the issue for IE, Electron, and UWP: https://github.com/firebase/firebase-js-sdk/pull/2464/files

Unfortunately we likely won't have any JS SDK releases for the next couple weeks due to US holidays. If anybody needs the fix sooner, I can likely help create a manual build of the SDK or something.

@jburja

This comment has been minimized.

Copy link

@jburja jburja commented Dec 20, 2019

@mikelehen I believe my team might need to get a fix sooner if you could create a manual build if possible. We are also using AngularFire within our application as a wrapper around the SDK but I'm not sure if that would require a new build as well?

mikelehen added a commit that referenced this issue Dec 20, 2019
…ader. (#2464)

There is a backend bug (b/145624756) causing $httpHeaders to be ignored if the HTTP request doesn't have an Origin header.  Via #1491 we've found that several environments are running into this issue so until the backend issue is fixed we are excluding them from using $httpHeaders. This may incur an extra CORS preflight round-trip in some cases, but this is much better than having our Authorization header get ignored.
@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Dec 20, 2019

@jburja It turns out you can npm install firebase@canary to get the fix immediately. Let me know if you need any additional info.

@ShishKabab

This comment has been minimized.

Copy link

@ShishKabab ShishKabab commented Jan 6, 2020

#2464 unfortunately doesn't solve the problem yet for Firefox extensions. Don't know how to reliably detect that though.

@ShishKabab

This comment has been minimized.

Copy link

@ShishKabab ShishKabab commented Jan 6, 2020

We've worked around it in our Firefox extension with the following code (using 7.6.1-canary.af162f9), because we have to release a fix right now:

const firebaseUtil = require('@firebase/util')
if (firebaseUtil.isReactNative) {
    firebaseUtil.isReactNative = () => true
}

That's obviously not something that'll hold up for long :) Any ideas on how to reliably detect an FF extension with <all_urls>? Would be happy to contribute a fix.

@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Jan 6, 2020

Just poking around, I wonder if typeof extension !== 'undefined' would work to detect we're in a browser extension (https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/extension).

This may be over-broad (will likely detect any Firefox, Chrome, etc. extension), but that's probably okay. Can you verify that works and either let me know or submit a PR? Thanks!

@klaussner

This comment has been minimized.

Copy link

@klaussner klaussner commented Jan 6, 2020

I haven't tried it but maybe a combination of runtime.getBrowserInfo and permissions.contains could also work.

@ShishKabab

This comment has been minimized.

Copy link

@ShishKabab ShishKabab commented Jan 7, 2020

Just did a quick test, seems like it may work:
Screenshot_20200107_121803

@ShishKabab

This comment has been minimized.

Copy link

@ShishKabab ShishKabab commented Jan 8, 2020

I tried to prepare a PR, but we'd need to check the result of the browser.permissions.getAll() promise in the openStream method of WebChannelConnection, which is currently not returning a promise. How should we go about this?

Also, I didn't do an npm install yet, but in case there's no typings present for the browser object, should I add them to the project, or just cast window to any and work from there?

@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Jan 13, 2020

@ShishKabab Thanks for the investigation. I think I'll probably err on the side of being more general and just try to detect that we're in any Firefox extension.

Per #2528 it sounds like Chrome 80+ extensions are hitting the same sort of issue, so I think I'll probably make a PR today that:

  • Detects Firefox extensions (via typeof browser === 'object' && browser.runtime)
  • Detects Chrome extensions (via typeof chrome == 'object' && chrome.runtime)

If anybody knows of any problems / limitations with that approach, let me know.

mikelehen added a commit that referenced this issue Jan 14, 2020
@scottfr

This comment has been minimized.

Copy link

@scottfr scottfr commented Jan 14, 2020

Thank you for these suggestions Mike. I have patched the build of Firestore in our extension to simply delete the setting of request.httpHeadersOverwriteParam = '$httpHeaders'.

This appears to resolve the issue.

We are on Firebase 6.2.4 and would prefer to not rush out an upgrade to the newest Firebase SDK without a lengthier testing process. Will this small request.httpHeadersOverwriteParam = '$httpHeaders' removal cause any issues on older versions of Firestore?

In regards to your specific suggested detection logic, "typeof chrome == 'object' && chrome.runtime" does evaluate to truthy in Chrome Extensions but it also evaluates to truthy in regular webpages on Chrome. This https://stackoverflow.com/questions/7507277/detecting-if-code-is-being-run-as-a-chrome-extension says you should be checking for chrome.runtime.id too.

======

Stepping back, I think the backend issue should be a high priority to be fixed as quickly as possible. This is not Firestore's fault, but previously working Firestore integrations are going to break when these changes roll out in Chrome.

Chrome Extension reviews by Google can take up to 30 days or longer. So even if we submitted a fix today, it may not reach our users before Chrome 80.

@mikelehen

This comment has been minimized.

Copy link
Member

@mikelehen mikelehen commented Jan 14, 2020

@scottfr Thank you! Good catch about my check accidentally detecting normal chrome too! I've updated it to use id as you suggested and it seems to work reliably in Chrome and Firefox now.

As per your question, it should be completely safe to remove request.httpHeadersOverwriteParam = '$httpHeaders' so you can move forward with that as a manual fix.

And thanks for highlighting the impact that this is going to have once Chrome rolls out their changes. I'd definitely love to get the backend fixed sooner, but realistically this is challenging. I've started some internal discussion to see what we can do though. Thanks for your diligence in reporting this issue!

mikelehen added a commit that referenced this issue Jan 14, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.