The New Default. Your hub for building smart, fast, and sustainable AI software
Table of Contents
and 6 more
Authentication is one of the first architectural decisions a Vue.js team makes – and one of the most consequential. Get it wrong, and you're retrofitting security into a codebase that wasn't designed for it. Get it right, and you have a clean, maintainable pattern that scales as the application grows.
Vue.js and other frontend frameworks were designed to tackle the challenges and inefficiencies of traditional web development. Just as these frameworks simplify UI development, other areas of web development have also evolved to become more accessible and efficient. Today, creating a scalable project does not necessarily require an exclusive, expensive, custom-built backend system to handle common server-side challenges.
This is where Backend as a Service (BaaS) solutions come into play, with Firebase standing out as one of the top options. Backed by Google's cloud infrastructure, Firebase offers a quick, secure, and reliable solution for managing authorization in web apps.
In this post, we'll build a secure and scalable Firebase login and registration system with Vue.js, covering authentication state management and third-party sign-in. If you'd rather have a team build it than build it yourself, Monterail's Vue.js development team has shipped this exact pattern in production.
Executive Summary
Firebase Authentication handles identity. It issues the token when someone signs in, and keeps that session alive across page reloads without any extra work on your end. It also plugs into whichever OAuth provider you choose. None of that logic has to live in your app, which frees your team to focus on business logic instead. This tutorial builds a complete Vue.js authentication system step by step: Firebase project setup, email/password auth, a Pinia store for reactive auth state, logout handling, session expiry context, and Google Sign-In as a bonus. The same pattern applies to any Firebase-supported OAuth provider. All code uses Vue 3 Composition API (
<script setup>) and TypeScript. If you're introducing Pinia for the first time, Vue.js modular state management covers store structure and testing patterns this tutorial doesn't have room for.
What's the Business Case for Firebase Authentication in Vue.js?
Firebase Authentication and Vue.js work well together to provide a secure, scalable solution for managing user authorization.
Firebase handles email/password authentication and third-party sign-in, and manages user state alongside them. That's enough to build a reliable authentication system without standing up or maintaining a custom backend.
By using Vue.js for dynamic UI updates and Firebase for authentication services, teams can focus on delivering a smooth user experience rather than server-side infrastructure.
Firebase eliminates the need to store sensitive information like tokens in the browser – it handles session persistence automatically.
A Guide to Setting Up Firebase for Your Vue.js Project
You can start working with Firebase right after setting up your fresh Vue.js project, and it will work after a few minutes of configuration.
To demonstrate the convenience of Firebase Authentication, we will build a basic Vue.js Firebase login and registration form.
)
How to Set Up Your Firebase Account
Head to the Firebase Console. Since Google provides Firebase, you'll only need to sign in with your Google account. Click + Create a project to start.
The interactive prompts will guide you through:
Your new project's name – as long as it's free, you can name it anything.
Google Analytics – not required for Firebase Authentication, so you can skip it for now.
)
How to Set Up User Authentication in Firebase Console
Navigate to "Build > Authentication" and click "Get started" to add your first sign-in method.
)
Choose Email/Password, enable it, and save the settings.
)
)
You can also enable email link sign-in (passwordless) here. For now, email/password is sufficient. That's all the Firebase Console setup needed. Let's return to our Vue.js project.
How to Organize Your Vue.js Project for User Authentication
Let's integrate Firebase into your Vue.js project to establish a fully functional authentication system. We'll install the Firebase SDK, set up the configuration, and initialize Firebase Auth.
If your app is growing beyond a single login form, component structure and project organization is worth a closer look before things get tangled.
Install Firebase in Your Vue.js Project
npm install firebase
This single package includes everything needed for authentication and other Firebase features.
Set Up Your Firebase App
Create /src/plugins/firebase.ts. Using TypeScript is strongly recommended:
// plugins/firebase.ts
import { initializeApp } from 'firebase/app';
// example configuration - remember to apply your own!
const firebaseConfig = {
apiKey: 'YOUR_API_KEY',
authDomain: 'YOUR_PROJECT_NAME.firebaseapp.com',
projectId: 'YOUR_PROJECT_NAME',
storageBucket: 'YOUR_PROJECT_NAME.firebasestorage.app',
messagingSenderId: '0000000000000',
appId: '0:000000000:web:000000000000000',
};
const app = initializeApp(firebaseConfig);
To find your configuration data:
Open your project in the Firebase Console.
Click the gear icon and choose "Project settings".
Scroll to Your apps and click the web icon "</>".
Add a nickname and click "Register app" – Firebase will give you the config to copy.
REMEMBER: This setup data (especially your apiKey) is sensitive. Do not push it into any public repository. Use Environment Variables instead.
Initialize the Firebase Auth Instance
// plugins/firebase.ts
import { initializeApp } from 'firebase/app';
import {
getAuth,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
} from 'firebase/auth';
const firebaseConfig = {
apiKey: 'YOUR_API_KEY',
authDomain: 'YOUR_PROJECT_NAME.firebaseapp.com',
projectId: 'YOUR_PROJECT_NAME',
storageBucket: 'YOUR_PROJECT_NAME.firebasestorage.app',
messagingSenderId: '0000000000000',
appId: '0:000000000:web:000000000000000',
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
Create Login and Register Functions
// plugins/firebase.ts
import { initializeApp } from 'firebase/app';
import {
getAuth,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
} from 'firebase/auth';
const firebaseConfig = {
// ...
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
export const createUser = async (email: string, password: string) => {
try {
const userCredential = await createUserWithEmailAndPassword(auth, email, password);
console.log('User created:', userCredential.user);
} catch (error) {
console.error('Error creating user:', error);
}
};
export const login = async (email: string, password: string) => {
try {
const userCredential = await signInWithEmailAndPassword(auth, email, password);
console.log('User signed in:', userCredential.user);
} catch (error) {
console.error('Error signing in:', error);
}
};
How to Create the Login and Registration Form with Firebase
You can set up your form with any Vue.js tools and style it however you like. For this demo we skip UI libraries and form validation to focus on Firebase.
// App.vue
<template>
<main class="content">
<form @submit.prevent class="login-form">
<input
v-model="email"
type="email"
placeholder="Your email..."
class="login-input" />
<input
v-model="password"
type="password"
placeholder="Your password..."
class="login-input"
/>
<div class="buttons">
<button type="submit" @click="signIn" class="submit-button">
Sign in
</button>
<button type="submit" @click="signUp" class="submit-button">
Sign up
</button>
</div>
</form>
</main>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { login, createUser } from './plugins/firebase';
const email = ref<string>('');
const password = ref<string>('');
const signIn = async () => {
if (!email || !password) {
return;
}
await login(email.value, password.value);
};
const signUp = async () => {
if (!email || !password) {
return;
}
await createUser(email.value, password.value);
};
</script>
The form:
Adds two input fields bound to reactive
emailandpasswordrefs viav-modelCreates two functions that pass values into the Firebase functions
Connects two buttons to
signInandsignUpvia@click
After creating a user, you'll see their data in the console and in the Firebase Console:
)
)
How to Implement Authentication State Management with Firebase
Firebase provides onAuthStateChanged to observe sign-in state changes. Import it and connect it to your app:
// App.vue
import { auth } from './plugins/firebase';
import { onAuthStateChanged } from 'firebase/auth';
onAuthStateChanged(auth, (user) => {
email.value = '';
password.value = '';
});To save state across the app, introduce Pinia for global state management. Create a user store:
// stores/userStore.ts
import { ref, computed } from 'vue';
import { defineStore } from 'pinia';
import type { User } from 'firebase/auth';
export const useUserStore = defineStore('user', () => {
const user = ref<User | null>(null);
const isLoggedIn = computed<boolean>(() => !!user.value);
const userName = computed<string | null>(() => {
if (user.value) {
return user.value?.displayName || user.value?.email
}
return null;
});
const setUser = (userData: User | null) => (user.value = userData);
return { user, isLoggedIn, userName, setUser };
});Then update App.vue to use the store:
<template>
<main class="content">
<form @submit.prevent class="login-form">
<h2 v-if="userStore.isLoggedIn">
Welcome, {{ userStore.userName }}
</h2>
<template v-else>
<input v-model="email" type="email" placeholder="Your email..." class="login-input" />
<input
v-model="password"
type="password"
placeholder="Your password..."
class="login-input"
/>
</template>
<div class="buttons">
<button type="submit" @click="signIn" class="submit-button">
Sign in
</button>
<button type="submit" @click="signUp" class="submit-button">
Sign up
</button>
</div>
</form>
</main>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { auth, login, createUser } from './plugins/firebase';
import { useUserStore } from './stores/userStore';
import { onAuthStateChanged } from 'firebase/auth';
const userStore = useUserStore();
const email = ref<string>('');
const password = ref<string>('');
const signIn = async () => {
if (!email || !password) return;
await login(email.value, password.value);
};
const signUp = async () => {
if (!email || !password) return;
await createUser(email.value, password.value);
};
onAuthStateChanged(auth, (user) => {
userStore.setUser(user || null);
email.value = '';
password.value = '';
});
</script>How it works:
Signing in triggers
onAuthStateChangedThe observer saves user data to the store via
setUser()Vue reactivity propagates
isLoggedInanduserNameacross the templateThe UI updates to show the welcome message
)
How to Log Out Users and Handle Session Expiry
Set Up the Logout Function
// plugins/firebase.ts
import { initializeApp } from 'firebase/app';
import {
getAuth,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
} from 'firebase/auth';
const firebaseConfig = {
// ...
};
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export const createUser = async (email: string, password: string) => {
// ...
};
export const login = async (email: string, password: string) => {
// ...
};
export const logout = async () => {
try {
await signOut(auth);
console.log('User signed out');
} catch (error) {
console.error('Error signing out:', error);
}
};In App.vue, add conditional buttons:
// App.vue
<div v-if="userStore.isLoggedIn" class="buttons">
<button type="submit" @click="logout" class="submit-button">Sign out</button>
</div>
<div v-else class="buttons">
<button type="submit" @click="signIn" class="submit-button">Sign in</button>
<button type="submit" @click="signUp" class="submit-button">Sign up</button>
</div>
// import
import { auth, login, createUser, logout } from './plugins/firebase';)
)
Manage Session Expiry
As the Firebase documentation states, "Firebase Authentication sessions are long-lived" – session expiry is not default behavior. Firebase SDK automatically expires a session only when:
The user is deleted
The user is disabled
Major user data changes (password reset, email change)
For custom session expiry, you'll need to integrate Firebase Realtime Database and its Security Rules – a topic for a follow-up post.
Bonus: How to Add Google Sign-In to Your Firebase Authentication Setup
In Firebase Console, choose the Google sign-in method, enable it, select a support email, and save:
)
// plugins/firebase.ts
import { initializeApp } from 'firebase/app';
import {
getAuth,
GoogleAuthProvider,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signInWithPopup,
signOut,
} from 'firebase/auth';
const firebaseConfig = {
// ...
};
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
const provider = new GoogleAuthProvider();
export const createUser = async (email: string, password: string) => {
// ...
};
export const login = async (email: string, password: string) => {
// ...
};
export const loginWithGoogle = async () => {
try {
const userCredential = await signInWithPopup(auth, provider);
console.log('User signed in:', userCredential.user);
} catch (error) {
console.error('Error signing in:', error);
}
};
export const logout = async () => {
// ...
};
In App.vue:
// template
<button type="submit" @click="loginWithGoogle" class="submit-button">
Sign in with Google
</button>
// script
import { auth, login, createUser, loginWithGoogle, logout } from './plugins/firebase';)
Key Takeaways
Firebase owns identity end to end, handling the token's entire lifecycle; your Vue.js app only reacts to the state it reports.
onAuthStateChangedfires on every sign-in and sign-out, including token refreshes – wire your Pinia store updates there, not into button handlers.Pinia's reactive
computedproperties (isLoggedIn,userName) push auth state to any component using the store, no prop drilling or event buses needed.Firebase sessions are long-lived by default; apps needing stricter expiry (banking, healthcare) must add custom logic via Firebase Realtime Database Security Rules.
Google Sign-In (and any other OAuth provider) swaps in
signInWithPopuporsignInWithRedirect– everything else in the pattern stays the same.
Why Keep Firebase and Pinia Responsibilities Separate?
Firebase owns identity: it issues the token, and then keeps it valid for as long as the user stays logged in. Your components never have to touch that logic. Pinia owns app state and exposes it reactively, so no component needs to know how auth actually happens under the hood. That separation is what makes the system easy to extend later. Adding a new OAuth provider means dropping in a new sign-in method, nothing more. A new role check or route guard lives in Pinia and Vue Router, never touching the identity layer underneath.
The trade-off worth remembering is that Firebase's defaults (long-lived sessions, client-side token refresh) are built for speed, and that works fine until compliance or audit requirements show up. Teams building toward those needs should plan for the gap early, before the architecture is already load-bearing.
Vue.js Authentication and Authorization FAQ
)


)

