firebase-tools
firebase-tools copied to clipboard
Firebase emulator does not work when exposing site via tunnel (i.e. localtunnel). Missing header Access-Control-Allow-Private-Network
[REQUIRED] Environment info
firebase-tools: 10.2.1
Platform: Windows 10
[REQUIRED] Test case
Attempt to create a user and sign them in when file is exposed via a tunnel.
- Setup a firebase project with hosting and emulators
- Replace index.html in public folder with below
- Emulate project
<!DOCTYPE html>
<html lang="en">
<head>
<title>App</title>
</head>
<body>
<body>
<div>
<button id="createUser">Create User</button>
<button id="authenticateUser">Authenticate User</button>
</div>
<!-- Insert this script at the bottom of the HTML, but before you use any Firebase services -->
<script type="module">
import { initializeApp } from 'https://www.gstatic.com/firebasejs/9.6.7/firebase-app.js'
// Add Firebase products that you want to use
import {
getAuth,
connectAuthEmulator,
signInWithEmailAndPassword,
createUserWithEmailAndPassword,
} from 'https://www.gstatic.com/firebasejs/9.6.7/firebase-auth.js'
const email = "[email protected]"
const password = "password"
const firebaseConfig = {
apiKey: "AIzaSyDrzYTAKem9ut-r_iV-vIDZggnsjvttfQM",
authDomain: "fir-emulator-test-40bf3.firebaseapp.com",
projectId: "fir-emulator-test-40bf3",
storageBucket: "fir-emulator-test-40bf3.appspot.com",
messagingSenderId: "1033607461892",
appId: "1:1033607461892:web:4bfee036d7c11e4f7a4951"
};
const app = initializeApp(firebaseConfig);
const auth = getAuth()
connectAuthEmulator(auth, "http://localhost:9099");
async function signOut () {
console.log("Signing out");
await auth.signOut()
console.log("Signed out");
}
async function authenticateUser () {
await signOut()
console.log("Signing in");
const credential = await signInWithEmailAndPassword(auth, email, password)
console.log("Signed in", credential)
}
async function createUser () {
console.log('Creating User');
const credential = await createUserWithEmailAndPassword(auth, email, password)
console.log("User created", credential)
}
auth.onAuthStateChanged(console.log)
document.getElementById('createUser').addEventListener('click', createUser, true);
document.getElementById('authenticateUser').addEventListener('click', authenticateUser, true);
</script>
</body>
</body>
</html>
[REQUIRED] Steps to reproduce
- Navigate to localhost:5000 (or your path for emulated hosting)
- Click "Create User"
- Verify user was created
- Click "Authenticate User"
- Verify user was authenticated.
- Use localtunnel to expose your site
npx localtunnel --port 5000
(https://github.com/localtunnel/localtunnel) - Navigate to url output by localtunnel
- Click "Authenticate User" (no need to create a second time as should already exist from step 2)
[REQUIRED] Expected behavior
User should be authenticated when you click "Authenticate User"
[REQUIRED] Actual behavior
Attempt fails with error message
Access to fetch at 'http://localhost:9099/identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=AIzaSyDrzYTAKem9ut-r_iV-vIDZggnsjvttfQM' from origin 'https://test.loca.lt' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Private-Network' header was present in the preflight response for this private network request targeting the `local` address space.
From reading it looks like it could be related to https://developer.chrome.com/blog/private-network-access-update/ ??
It looks like you're trying to expose a site to the public Internet, while still accessing the local Firebase Auth emulator. If this is the case, even if Chrome does not block the request, your request to the Auth emulator will still fail since it is trying to access localhost
from a different device / network.
A more reasonable setup will be to expose Auth Emulator in addition to the website that access it. I think this goes straight into the area of tunnel configuration and network setup, which you'll find better help at StackOverflow.
Hi @yuchenshi I really appreciate your response. We are exposing the local firebase emulator as we are developing embedded apps and need to preview the apps within an iframe of another app. The app is running and being accessed from the local machine. There is no intent to make a developed site publicly accessible. This has been working, and we have been developing like this for years. It has only recently stopped working for our projects.
We have managed to get auth working by exposing it via a separate tunnel but cannot get Firestore working yet - it just times out. This also means that we need to run up to 6 separate tunnels just to emulate. I know that is our problem and outside of firebase-tool but just communicating the rationale.
I know you guys are busy and this may be a case of "that's not really how it works dude" but is adding the Access-Control-Allow-Private-Network
header something that could be considered.
Thank you for taking the time. We love the emulators - they have completely changed how we develop with Firebase. Truly appreciate how developer centric you guys are.
EDIT: Looks like cors (used in the emulators) is already considering including the header as an option https://github.com/expressjs/cors/issues/236. There doesn't seem to have been much movement though. Would be so grateful if adding the header is something you could consider. It's really hampering us.
Thanks for sharing the context and let me reopen this issue to talk more about the options here.
First of all, have you tried modifying the Auth Emulator code to add the header and see if it works for you? It may sound scary, but it is not -- the whole emulator is open source, and the CONTRIBUTING guide is easy to follow and contains the typical npm package dev setup. I see you've done some research on cors -- this is where it is used in Auth Emulator: https://github.com/firebase/firebase-tools/blob/5a8bd4e4337536821a258fa7d14cdc11d431c478/src/emulator/auth/server.ts#L125
Try adding a middleware next to it that sets the header and see if it works. If it does, it unblocks your local setup immediately while you send us a PR and we'll be glad to review it. If things go smoothly until this point, then our team will be in a much easier position to consider similar PRs to all our Node.js-based emulators.
P.S. The Java emulators are not open source and may require more process and work on our side, but you'll always have the workaround of running a reverse proxy to add that header while waiting for the changes to happen. For example, https://www.npmjs.com/package/cors-anywhere can be used to reverse proxy while adding any headers (despite its main focus on adding CORS-related headers).
Legend, thank you so much.
I have done some testing and a middleware worked. I haven't tried on any projects but worked on the example above. I placed it before the cors middleware in the auth server - see below
app.use((req, res, next) => {
if (req.headers["access-control-request-private-network"]) {
res.setHeader("access-control-allow-private-network", "true");
}
next(null);
})
I'll look at doing a PR. I know Firestore also complains - where are the cors options for Firestore, RTDB and Storage etc? Is it hub.ts
I need to update?
https://github.com/firebase/firebase-tools/blob/5a8bd4e4337536821a258fa7d14cdc11d431c478/src/emulator/hub.ts#L75
Interestingly I tried setting up a proxy using cors-anywhere
but could not get it to work. You can't set response headers with cors-anywhere
(despite a lot of discussion about the feature) but it was a one-line hack to add the header.
However, for some reason when I proxy to localhost:9099
it complains about SSL. I couldn't see where this error was originating to work out why it was complaining or if my cors-anywhere
config was off.
Any ideas? It would be good to get a simple proxy running as a solution while we look at a PR
Appreciate your help.
Firestore, RTDB and Storage emulators are not open source and I can file a feature request internally for those. Again, this will be a longer process and I'm glad to hear you're looking into workarounds in the meantime.
I think I've misremembered about cors-anywhere
and I no longer recommend using it as part of the solution. Also, this is the first time I've ever seen the error message ssl-is-required-to-perform-this-operation
and I believe you must have somehow connected to production Firebase Auth APIs during the process. I'd recommend finding another solution that does not involve cors-anywhere
.
I'd have to admit that I don't have much experience in local reverse proxies here so take my advise with a grain of salt. I am certain that something like nginx is flexible enough for this use case, but it requires a bit of setup and configuration. I don't know if there's a lighter weight solution but at this point we're looking at a generic problem (a reserve proxy that supports adding response a header) better suited for other forums (e.g. StackOverflow).
Thank you. I appreciate the response.
I'll find some time to do a PR on the Auth emulator. I can confirm it has resolved the issue for us so would be grateful if you can do a feature request for the others.
That is ok I know work arounds are outside the scope of firebase-tools
but I appreciate the help nonetheless. I'll see if we can hack something together until you can get the updates into the emulators.
@pklitscher One small clarification though -- although the Storage Emulator is not fully open source, the API server part is. Feel free to add similar logic here and include the diff in the PR you're working on:
https://github.com/firebase/firebase-tools/blob/a53828189dcdfe3dcdc2240b3a0b4c348b043a5b/src/emulator/storage/server.ts#L28-L29
Firestore, RTDB and Storage emulators are not open source and I can file a feature request internally for those.
Hey @yuchenshi any timeline on getting this feature in for Firestore and RTDB? Small change that would make life so much easier and already demonstrated via the above PR.
Thanks so much for your help
@pklitscher please try getting your PR merged first. That will be a good demonstration, especially after it lives in the releases for a while without affecting anyone else's use case. We'll then consider adopting the final merged code changes to other emulators.
@yuchenshi so sorry I completely missed that the PR had a conflict in the CHANGELOG. Resolved for merge now
Hey @yuchenshi
How are we looking for getting this rolled into Firestore and RTDB? The PR has been merged for a while; how long would we like to have that out? We've been using the Auth and Storage edits for a year.
We are limping along using proxies to expose Firestore and RTDB. But localtunnel is no longer maintained and becoming unstable, ngrok didn't work, and we even tried rolling our own proxy using Cloudflare but for the life of me we can't seem to get the databases working across it. We have 5 apps in production we are trying to support that require proxies to develop.
Hoping we can work towards merging into the other emulators soon, especially given it's only an additional header.
Really appreciate your help.
@pklitscher I'm no longer on the team, but I did implement it in the Firestore emulator before I was gone. Feel free to check it out and let us know how it goes.
@IanWyszynski can probably comment on the RTDB emulator. I'd expect it will be harder due to the code structure though.
Thank you @yuchenshi, I will check it out.
@IanWyszynski love to see this in RTDB if possible. Would help us so much. We use Firestore mostly but still use RTDB for a number of things in projects.
@pklitscher would you be able to pull this branch to see if the latest emulator version works for you: https://github.com/firebase/firebase-tools/pull/5543?
@pklitscher would you be able to pull this branch to see if the latest emulator version works for you: #5543?
@IanWyszynski Sorry for the delay getting back to you, we are crazy at the moment.
I tested it out but seem to be getting a CORS error. I don't remember CORS ever being an issue
I did try a test request to an open path in the rtdb and the Access-Control-Allow-Origin: *
was present. Not sure if I am doing something wrong here.
localtunnel with firebase emulator works on linux/ubuntu
I ended up just using WSL to make it work with Windows.