Test Firebase Authentication locally with authorized domains
Test Firebase Authentication sign-in on localhost with a Horizon tunnel, and add the tunnel host to the authorized domains once.
Sign in with Firebase Authentication on your laptop, from a public HTTPS host that Firebase accepts.
Firebase only completes sign-in from domains on your project's authorized domains list. Firebase adds localhost by default. A Horizon host isn't on the list until you add it.
Before you begin
- Node.js 18 or later
- A Horizon account and the CLI (see Getting started)
- A reserved subdomain for
-s. Reserve one on the Subdomains page. - A Firebase project with a sign-in provider enabled. This page uses Google.
- A web app that uses the Firebase JavaScript SDK
Start a tunnel
Use -s with a subdomain you reserved. Without it, the subdomain is random and changes every run, so the URLs you save in Firebase would point at a dead URL after a restart. Reserved subdomains are a paid feature, see Pricing.
hrzn tunnel http://localhost:3000 -s my-appHORIZON: Tunnel connected
URL https://my-app.hrzn.run (reserved)
Forwarding http://localhost:3000
Request log https://hrzn.run/dashboard/tunnels/my-appYour public URL is https://my-app.hrzn.run. Keep this terminal open.
The first time you open the URL in a browser, Horizon shows a Before you continue page. Select Continue to site. You see it once every 7 days per IP address, so the redirect back from Firebase won't surprise you later.
Add the domain in Firebase
- Open the Firebase console and select your project.
- Open Authentication, then Settings.
- Select Authorized domains, then add
my-app.hrzn.run.
Enter the host only. Leave out https:// and any port.
Set up the app
Install the SDK.
npm install firebaseInitialize Firebase with the config from your project settings. Firebase's own web setup uses a firebaseConfig object, so there are no environment variables to set.
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
const firebaseConfig = {
// Your config values here
};
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);Sign in with a popup. The popup flow works with the default authDomain of your Firebase project, so the tunnel host only needs to be an authorized domain.
"use client";
import { GoogleAuthProvider, signInWithPopup } from "firebase/auth";
import { auth } from "@/lib/firebase";
export function SignInButton() {
const signIn = async () => {
const result = await signInWithPopup(auth, new GoogleAuthProvider());
console.log(result.user.uid);
};
return <button onClick={signIn}>Sign in with Google</button>;
}Render SignInButton on a page, then start the app on port 3000.
npm run devIf you use the redirect flow
signInWithRedirect() is the harder path on a custom host. Firebase says it relies on a cross-origin iframe to your project's Firebase Hosting domain, and that this fails in browsers that block third-party storage access. Firebase's fix for a custom domain is to set that domain as authDomain. This makes the app and the auth iframe share a domain.
- Set
authDomaintomy-app.hrzn.runinfirebaseConfig. - Add
https://my-app.hrzn.run/__/auth/handlerto the authorized redirect URIs of each OAuth provider. - Make your app serve the Firebase helper files under
/__/auth/. Firebase lists two ways: forward requests for/__/auth/tohttps://<project>.firebaseapp.com/__/auth/with a reverse proxy, or self-host the helper files. Self-hosting doesn't work for Apple sign-in or SAML.
Use the popup for local testing unless you need to test the redirect flow itself.
Sign in through the public URL
Open https://my-app.hrzn.run and select Sign in with Google. Start from the public URL, because Firebase checks the domain you sign in from.
Check it works
The Google popup opens, and it closes after you pick an account. The uid of the signed-in user prints in the browser console.
Your Horizon terminal prints a GET line for the page you loaded. Firebase handles the sign-in handshake itself, so you won't see a callback line unless you use the redirect flow.
Troubleshooting
Firebase says the domain isn't authorized
Firebase returns auth/unauthorized-domain when the domain of the sign-in isn't on the authorized domains list. Add my-app.hrzn.run under Authentication, Settings, Authorized domains. Enter the host without https:// or a port.
Sign-in with redirect fails or loops
Your browser may block third-party storage, which breaks the default redirect flow. Set authDomain to my-app.hrzn.run and serve /__/auth/, as above. Or switch to signInWithPopup().
The provider rejects the redirect URI
With a custom authDomain, add https://my-app.hrzn.run/__/auth/handler to the provider's authorized redirect URIs.
The tunnel URL changed
You started the tunnel without -s, so Horizon gave you a new random subdomain. Restart with -s my-app and the URL stays the same. -s needs a subdomain you reserved, see Pricing.
Nothing reaches your app
- Check that the Horizon terminal is still running. If its last line is
Connection lost. Reconnecting…, wait forReconnected. - Check that your app listens on port 3000.
Next steps
- Read Firebase's Google sign-in guide for JavaScript.
- Read Firebase's redirect best practices.
Test Auth0 sign-in locally with a public callback URL
Test Auth0 sign-in on localhost with a Horizon tunnel, and set Allowed Callback URLs, Allowed Logout URLs and Allowed Web Origins once.
Test Kinde sign-in locally with a public callback URL
Test Kinde sign-in on localhost with a Horizon tunnel, using a stable HTTPS URL for the allowed callback and logout redirect URLs.