Firebase Google sign-in with Vue 3 and Nuxt

Updated Oct 27, 2026 · Pewterkit Guides

For Vue 3, install VueFire with its VueFireAuth() module and use useFirebaseAuth() plus useCurrentUser() in components; for Nuxt, use the VueFire Nuxt module (or a .client plugin) so Firebase Auth only runs in the browser. To make Google's screen show your domain on a Nuxt server deployment, add a routeRules proxy from /__/auth/** to YOUR-PROJECT.firebaseapp.com and set authDomain to your domain.

Why Google sign-in fails in Vue and Nuxt apps

  • Nuxt renders on the server first. VueFire's docs say useFirebaseAuth() is null on the server, so a sign-in call that runs during SSR (in setup at top level, for example) fails. Popup sign-in belongs in a click handler.
  • "No user" vs. "not loaded yet". useCurrentUser() is undefined until Firebase has restored the session, then null or a user. Treating undefined as signed out causes flicker and wrong redirects.
  • Route guards that run too early. VueFire asks you to register the auth-aware guard before app.use(router) so the first navigation waits for the user.
  • Unlisted hosts. Nuxt's dev server runs at http://localhost:3000; a plain Vue + Vite app at localhost:5173. Projects created after April 28, 2025 don't get localhost as an authorized domain by default.
  • Redirect sign-in. Firebase says signInWithRedirect() needs one of its workarounds on Chrome M115+ (from June 24, 2024), Firefox 109+ and Safari 16.1+.

Step-by-step fix: Vue 3 (Vite) with VueFire

Install the packages:

npm i vuefire firebase

Register VueFire with the Auth module:

// src/main.ts
import { createApp } from 'vue';
import { initializeApp } from 'firebase/app';
import { VueFire, VueFireAuth, getCurrentUser } from 'vuefire';
import App from './App.vue';
import router from './router';

const firebaseApp = initializeApp({
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
  appId: import.meta.env.VITE_FIREBASE_APP_ID,
});

const app = createApp(App);
app.use(VueFire, { firebaseApp, modules: [VueFireAuth()] });

// Add the guard before app.use(router) so the first navigation is covered
router.beforeEach(async (to) => {
  if (to.meta.requiresAuth) {
    const user = await getCurrentUser();
    if (!user) return { path: '/login', query: { redirect: to.fullPath } };
  }
});

app.use(router);
app.mount('#app');

Use the composables in a component:

<!-- src/components/GoogleSignIn.vue -->
<script setup lang="ts">
import { GoogleAuthProvider, signInWithPopup, signOut } from 'firebase/auth';
import { useCurrentUser, useFirebaseAuth } from 'vuefire';

const auth = useFirebaseAuth(); // null during server rendering in Nuxt
const user = useCurrentUser(); // undefined until loaded, then User or null
const provider = new GoogleAuthProvider();

function signIn() {
  signInWithPopup(auth!, provider).catch((reason) => {
    console.error('Google sign-in failed', reason);
  });
}
</script>

<template>
  <span v-if="user === undefined">Loading…</span>
  <button v-else-if="user" @click="signOut(auth!)">Sign out {{ user.displayName }}</button>
  <button v-else @click="signIn">Sign in with Google</button>
</template>

If you'd rather not add VueFire, the same pattern works with a hand-written composable: call onAuthStateChanged() once in a module, store the user in a ref, and return it from useAuthUser().

Step-by-step fix: Nuxt with the VueFire module

Install it:

npm install firebase
npx nuxi@latest module add vuefire

Configure it in nuxt.config.ts:

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-vuefire'],
  vuefire: {
    auth: { enabled: true },
    config: {
      apiKey: 'your-web-api-key',
      authDomain: 'YOUR-PROJECT.firebaseapp.com', // switch to your domain once /__/auth is proxied (see below)
      projectId: 'YOUR-PROJECT',
      appId: '1:000000000000:web:abc123',
    },
  },
});

Firebase says API keys restricted to Firebase services don't need to be secret, so these values can live in the config. The GoogleSignIn.vue component above works unchanged in Nuxt. Protect pages with route middleware:

// middleware/auth.ts
export default defineNuxtRouteMiddleware(async (to) => {
  const user = await getCurrentUser();
  if (!user) {
    return navigateTo({ path: '/login', query: { redirect: to.fullPath } });
  }
});

VueFire warns that global middleware can loop, so only redirect when the target isn't already the login page. If you want the server to render pages for the signed-in user, VueFire's SSR auth features need firebase-admin, a service account exposed through GOOGLE_APPLICATION_CREDENTIALS, and optionally auth.sessionCookie: true. Client-side sign-in alone doesn't need them.

Alternative: a client-only Nuxt plugin

Without VueFire, a .client suffix keeps Firebase out of the server bundle, and public runtime config lets you change authDomain per environment:

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      firebaseApiKey: '',
      firebaseAuthDomain: '', // NUXT_PUBLIC_FIREBASE_AUTH_DOMAIN overrides this
      firebaseProjectId: '',
      firebaseAppId: '',
    },
  },
});
// plugins/firebase.client.ts
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';

export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig().public;
  const app = initializeApp({
    apiKey: config.firebaseApiKey,
    authDomain: config.firebaseAuthDomain,
    projectId: config.firebaseProjectId,
    appId: config.firebaseAppId,
  });
  return { provide: { auth: getAuth(app) } };
});

In components, const { $auth } = useNuxtApp() gives you the Auth instance, but only in the browser, so call it from event handlers or onMounted.

Authorize your hosts

In the Firebase console, enable Google under Security > Authentication > Sign-in method, then open Settings > Authorized domains and click Add domain (as of Oct 2026; older guides use Build > Authentication). Add localhost for development (Firebase recommends removing it from production projects), your production domain such as app.example.com, and your host's default domain if people sign in there.

Show your own domain on Google's sign-in screen

Firebase's docs say users may see Continue to: https://YOUR-PROJECT.firebaseapp.com during Google sign-in, because the SDK opens https://<authDomain>/__/auth/handler. Firebase's redirect best-practices page (option 3) says to forward /__/auth/ on your domain to YOUR-PROJECT.firebaseapp.com transparently (not via a 302), then change authDomain.

Nuxt deployed with a server (Node, serverless or edge preset). Nitro's proxy route rule proxies matching requests to another URL, and its docs show the /** form carrying the rest of the path across:

// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/__/auth/**': { proxy: 'https://YOUR-PROJECT.firebaseapp.com/__/auth/**' },
  },
});

Test this on a preview deployment before relying on it. Nitro's docs don't spell out how the proxy rule handles POST requests, which Firebase says must be forwarded too, and Nuxt's config reference marks routeRules as experimental, so re-check it after upgrades.

Static output (nuxi generate) or a plain Vue SPA. Prerendered files are served without your Nitro server, so put the forwarding on the host instead. The Netlify _redirects and vercel.json rules in the Vite guide linked below work the same way for a Vue build.

Then finish on Google's side:

  1. Google Cloud console > APIs & Services > Credentials > your web client under OAuth 2.0 Client IDs (as of Oct 2026 also under Google Auth Platform > Clients): add https://app.example.com/__/auth/handler to Authorized redirect URIs. Google says changes can take 5 minutes to a few hours.
  2. Confirm app.example.com is an authorized domain in Firebase.
  3. Set authDomain to app.example.com: in vuefire.config for the module setup, or with NUXT_PUBLIC_FIREBASE_AUTH_DOMAIN=app.example.com for the plugin setup. Keep YOUR-PROJECT.firebaseapp.com for local development unless you also proxy there.

How to confirm it worked

  1. Request https://app.example.com/__/auth/handler. You should get Firebase's response, not Nuxt's error page. An error page means the route rule isn't active on this deployment (a common cause is a static deploy).
  2. In DevTools > Network, start sign-in and check that the popup opens on https://app.example.com/__/auth/handler?....
  3. Google's account chooser should name app.example.com as the site you're continuing to.
  4. After sign-in, useCurrentUser() should change from undefined to your user, and protected routes should load without bouncing to /login.

FAQ

Is VueFire required for Firebase Auth in Vue 3?

No. VueFire adds composables like useCurrentUser() and getCurrentUser() for router guards, but the Firebase modular SDK works directly with your own ref and onAuthStateChanged().

Why is useFirebaseAuth() null in Nuxt?

VueFire returns null from useFirebaseAuth() on the server. Call sign-in and sign-out from browser events, and use a non-null assertion in TypeScript only where the code can't run during SSR.

Do I need a service account for Nuxt Firebase auth?

Only for VueFire's SSR auth features, such as rendering pages with the signed-in user or session cookies. Plain client-side Google sign-in doesn't need firebase-admin or a service account.

Will the routeRules proxy work with nuxi generate?

Don't count on it. A static deploy usually has no Nitro server at request time to run the rule, so use your host's rewrite feature for /__/auth/* instead, then request /__/auth/handler on the live site to confirm Firebase answers.

Sources

  1. Authenticate Using Google with JavaScript | Firebase Authentication
  2. Best practices for using signInWithRedirect on browsers that block third-party storage access | Firebase
  3. Authentication FAQ and troubleshooting | Firebase Authentication
  4. Learn about and manage API keys for Firebase
  5. firebase-js-sdk: packages/auth/src/core/util/handler.ts
  6. Getting Started | VueFire
  7. Firebase Authentication | VueFire
  8. Nuxt.js | VueFire
  9. Authentication | VueFire (Nuxt)
  10. plugins | Nuxt
  11. Runtime Config | Nuxt
  12. Nuxt Configuration | Nuxt
  13. nuxt dev | Nuxt
  14. Deployment | Nuxt
  15. Routing | Nitro
  16. Server Options | Vite
  17. Manage OAuth Clients | Google Cloud

How this page was made: drafted with AI from the sources listed, reviewed by Pewterkit. Docs checked Oct 27, 2026. Spotted something out of date? Tell us.

Related guides