Closed tomasszabo closed 4 years ago
I found a few problems with this issue:
I'm having the same issue
@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!?).
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.
@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?
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.
@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.
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;
}
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.
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.
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"
All of this with firebasejs 6.3.0
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.
@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!
I'm using 6.6.0, but this problem was present in 6.0.2 as well.
@mikelehen 6.6.0 doesn't fix the problem in IE 11
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.
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.
@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.
Hello @developius, Thanks for solutions. The solution saves my day.
@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.
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
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
51
[[0,["c","2A3yo6U6BtLH5jlrG9Qfmg","",8,12,30000]]]
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
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
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"]]]
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
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
51
[[0,["c","rILgOaemnhCrHGXkUgfTdg","",8,12,30000]]]
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
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
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"]]]
@klaussner Thank you! I will try to pull in some backend folks who may have insight into what's going on.
@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.
@mikelehen Transactions are working without an Origin header. 👍
Seeing the same conditions, if IE11 configures a domain in a trusted zone it wont add the origin header to a request. When you do a regular call to firestore you get permission denied( request.auth comes through as null) but if you run the request as a transaction then the rules are ran IE request.auth is populated). @mikelehen
@amammay Thanks for confirming. I've logged an internal bug against the backend team (145624756 for reference) with these details and we're waiting to hear back.
As an update, we understand the issue now and will see if we can get a clean solution on the backend eventually, but in the immediate future, the best workaround is probably to work around this in the SDK. It turns out we actually have a workaround for this already, targeted at React Native apps, and we can expand this to cover other environments that we know to be problematic.
The code in question is here: https://github.com/firebase/firebase-js-sdk/blob/bb1f4dee32dc4ee41066e176378e1b63aa1e1d14/packages/firestore/src/platform_browser/webchannel_connection.ts#L271
If anybody's interested in making PRs to exclude other environments besides ReactNative, that would be helpful. Else if you could at least provide details on broken environments and/or suggestions on how to best detect them (via user-agent checking, etc), then we can try to cobble them together into a PR.
Thanks!
This seems to be my exact problem. I don't have a problem with access when running the app with create-react-app but after building it with electron-builder, I get "permission denied". Is there any work-around I can use in my app or will I have to wait for a fix in Firebase SDK as mentioned above?
@agray5 I don't know of a workaround without modifying the SDK. FWIW, just commenting out this line would likely be enough: https://github.com/firebase/firebase-js-sdk/blob/bb1f4dee32dc4ee41066e176378e1b63aa1e1d14/packages/firestore/src/platform_browser/webchannel_connection.ts#L272
In fact, if somebody wants to do that experiment, it would be useful to verify the theory for sure.
@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.
@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.
@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 https://github.com/firebase/firebase-js-sdk/blob/bb1f4dee32dc4ee41066e176378e1b63aa1e1d14/packages/firestore/src/platform_browser/webchannel_connection.ts#L271
@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 😐
@amammay Yes, when the backend issue is resolved we can remove these checks, but that may not happen for some time unfortunately.
@mikelehen Please don't forget that this is not only Electron
issue, other environments like UWP
, Firefox
, etc. are also impacted.
@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 "
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.
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.
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).
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.
@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?
@jburja It turns out you can npm install firebase@canary
to get the fix immediately. Let me know if you need any additional info.
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.
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!
I haven't tried it but maybe a combination of runtime.getBrowserInfo and permissions.contains could also work.
Just did a quick test, seems like it may work:
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?
@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 https://github.com/firebase/firebase-js-sdk/issues/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:
typeof browser === 'object' && browser.runtime
)typeof chrome == 'object' && chrome.runtime
)If anybody knows of any problems / limitations with that approach, let me know.
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.
@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!
This looks like #1491 fixed the issue. If this is still ongoing then please reopen the issue.
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:
I'm always receiving:
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:
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:
Javascript Console output: