Making it all work together
Have you suddenly started having problems with Firebase Auth? It’s likely due to recent security changes in Chrome that had already been implemented in Safari and Firefox for a while, which have an impact on how Firebase signInWithRedirect
operates … or doesn’t.
While there are some recommended best practices to follow to work around the issue, some of them are easier said than done and you may find yourself feeling like a pin-ball bouncing from issue to issue.
So I thought I’d document what worked for me, so I’ll have something to refer to myself. This is aimed at SvelteKit but should apply to any web development based on Vite.
The issue is how to:
Of course you’ll need to replace [your-project-id]
in the examples with your actual Firebase project ID along with the rest of the config.
There are Vite plugins that promise to do this but I found these instructions to create a certificate for localhost worked best for me (and because I hate having to install extra dependencies).
Run this command in the project root:
openssl req -x509 -out certs/localhost.crt -keyout certs/localhost.key \
-newkey rsa:2048 -nodes -sha256 \
-subj '/CN=localhost' -extensions EXT -config <( \
printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")
Specify the certs in the server
block of your vite.config.ts
together with a proxy definition for the auth. Fun fact: if you’re trying to enable https and don’t include at least an empty proxy
block, you’ll get weird undici errors … fun times.
import { readFileSync } from 'fs'
export default defineConfig({
// other config ...
server: {
https: {
key: readFileSync('certs/localhost.key'),
cert: readFileSync('certs/localhost.crt'),
},
proxy: {
'/__/auth': {
target: 'https://[your-project-id].firebaseapp.com',
changeOrigin: true,
},
},
},
// other config ...
})
I always like to put my Firebase config into .env
files, because in production the infrastructure is automated and the configuratino comes from the environment that is populated using Terraform. This is for development though. Note that this is the config you get from the Firebase console:
.env
PUBLIC_FIREBASE_API_KEY="AIzaSyAxxxxxxxxxxxxxxxxxxxxxxxxx"
PUBLIC_FIREBASE_AUTH_DOMAIN="[your-project-id].firebaseapp.com"
PUBLIC_FIREBASE_DATABASE_URL="https://[your-project-id]-default-rtdb.firebaseio.com"
PUBLIC_FIREBASE_PROJECT_ID="[your-project-id]
PUBLIC_FIREBASE_STORAGE_BUCKET="[your-project-id].appspot.com"
PUBLIC_FIREBASE_MESSAGING_SENDER_ID="999999999999"
PUBLIC_FIREBASE_APP_ID="1:444444444444:web:aaaaaaaaaaaaaaaaaa"
PUBLIC_FIREBASE_MEASUREMENT_ID="G-JSHDFGJKDH"
then to use localhost for auth during development, we can just override that one setting to point to our dev server, so it will be the same host:
.env.development
PUBLIC_FIREBASE_AUTH_DOMAIN="localhost:5173"
SvelteKit makes loading this config easy and you can decide whether it makes sense to use a static env (build time from the files above) or a dynamic env if you have automated infrastructure or want to deploy the same built code to multiple environments, the code will be similar.
import {
PUBLIC_FIREBASE_API_KEY,
PUBLIC_FIREBASE_APP_ID,
PUBLIC_FIREBASE_AUTH_DOMAIN,
PUBLIC_FIREBASE_MEASUREMENT_ID,
PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
PUBLIC_FIREBASE_PROJECT_ID,
PUBLIC_FIREBASE_STORAGE_BUCKET,
} from '$env/static/public'
export const config = {
apiKey: PUBLIC_FIREBASE_API_KEY,
authDomain: PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: PUBLIC_FIREBASE_APP_ID,
measurementId: PUBLIC_FIREBASE_MEASUREMENT_ID,
}
However you load the config, you can use it to initialize the Client-Side Firebase SDK:
import { initializeApp } from 'firebase/app'
import { config } from './config'
export const app = initializeApp(config)
Another way to do it would be to hard-code all the config, but use the dev
flag to toggle which Firebase authDomain
to use.
At this point, the app should run using https on localhost, but when you try to sign in you’ll get a error because it’s now no longer using a configured redirect URL (they configure that for you when your Firebase project is created). It will look like this:
The fix for this is to go to the Google Cloud Credentials page for the project which will look like this:
Click the little edit icon to edit the OAuth 2.0 Client IDs (web client) entry in the middle and you’ll get to this page where you can add the localhost version of the existing Authorised Redirect URI:
In case you can’t read it, it’s https://localhost:5173/__/auth/handler
which is similar to the existing Firebase entry, but on localhost. This is what you also need to configure if you want to use a custom domain in production.
It doesn’t hurt to also add the localhost address as an Authorised JavaScript Origin too.
That should be everything needed to make localhost development work. Whether you need to do anything for production will depend on your hosting setup and whether you use a custom domain, but you can easily add a production proxy in the same way. I normally do that in a server.js
file which is the entry point to my app when using the SvelteKit node adapter:
server.js
import http from 'http'
import compression from 'http-compression'
import httpProxy from 'http-proxy'
import { handler } from './build/handler.js'
const compress = compression()
const proxy = httpProxy.createProxy()
http
.createServer((req, res) => {
compress(req, res, () => {
if (req.url?.startsWith('/__/auth/')) {
proxy.web(req, res, {
target: 'https://[my-project-id].firebaseapp.com',
secure: false,
})
} else {
handler(req, res, (err) => {
if (err) {
res.writeHead(500)
res.end(err.toString())
} else {
res.writeHead(404)
res.end()
}
})
}
})
})
.listen(8080)
Hope this helps!