Discover why a manual Firebase integration is key for Lovable projects. Learn step-by-step Firebase Auth setup with expert best practices.

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Firebase requires manual setup in Lovable because key Firebase steps must happen outside of app code (in Google/Firebase consoles) and require secure secrets and CLI operations that Lovable’s chat environment can’t run for you. You must create the Firebase project, enable providers/APIs, register OAuth redirect/authorized domains, and provision service-account credentials manually — then supply those values into Lovable’s Secrets UI or a synced GitHub repo. Lovable can edit your app files and add placeholders, but it cannot perform console actions, run the Firebase CLI, or safely retrieve private Google service-account keys for you.
// Please create file docs/FIREBASE_MANUAL_SETUP.md with the exact content below
// This file intentionally explains the manual steps required outside Lovable
# FIREBASE MANUAL SETUP (for Lovable projects)
// 1) Console steps (Firebase/GCP)
// - Create a Firebase project in the Firebase Console.
// - Enable the Authentication providers you need (Email, Google, etc.).
// - Add your Lovable preview domain(s) and published domain to "Authorized domains" and OAuth redirect URIs.
// - If using server-side functions, enable required Google APIs and create a service account with the needed roles.
// - Download the service-account JSON (keep it private).
// 2) What to store in Lovable Secrets UI
// - FIREBASE_API_KEY (public key)
// - FIREBASE_AUTH_DOMAIN
// - FIREBASE_PROJECT_ID
// - FIREBASE_APP_ID
// - FIREBASE_SERVICE_ACCOUNT (paste full service-account JSON here; mark as secret)
// - Any OAuth client secret values (if applicable)
// 3) Deploy notes (outside Lovable)
// - Deploying Firebase Functions or running the Emulator requires the Firebase CLI or gcloud CLI.
// - These steps must be run locally or in CI (use GitHub export/sync for CI deployment).
// - Example: firebase deploy --only functions (run on your machine or CI).
//
// 4) Important reminders
// - Do NOT commit service-account JSON to source control.
// - Ensure authorized domains include both preview and production domains used by Lovable.
// - Use Lovable Secrets UI to add the keys; reference them in code via environment variables or runtime secrets.
// Please create file .lovable/firebase-placeholders.md with the exact content below
// This file provides placeholders Lovable can safely keep in repo; replace at runtime via Secrets UI.
# Firebase placeholders for code
// Create a lightweight module or env example to be filled from Secrets UI or CI.
// Do NOT commit real keys here — replace with secret bindings in Lovable.
export const FIREBASE_CONFIG_PLACEHOLDER = {
// // Replace these with values set in Lovable Secrets UI or GitHub secrets
apiKey: "FIREBASE_API_KEY_FROM_SECRETS",
authDomain: "FIREBASE_AUTH_DOMAIN_FROM_SECRETS",
projectId: "FIREBASE_PROJECT_ID_FROM_SECRETS",
appId: "FIREBASE_APP_ID_FROM_SECRETS"
};
// For server-side Admin SDK, configure to load FIREBASE_SERVICE_ACCOUNT from Secrets UI.
This prompt helps an AI assistant understand your setup and guide you through the fix step by step, without assuming technical knowledge.
Use this prompt first (short summary)
Create a minimal Firebase Auth integration: client SDK init, an AuthContext + hook, Login page, and a simple server API route to verify ID tokens. Store client config and the service-account JSON in Lovable Secrets. Edit these files (paths below). Note: adding npm deps (firebase, firebase-admin) requires GitHub sync / local install — I indicate that step as "outside Lovable (terminal required)".
Paste this entire prompt into Lovable chat (Chat Mode). It should create files and update App.tsx.
// Please create the following files and edits in the project.
// Add client Firebase init and an Auth Provider + Login UI.
// Create src/lib/firebaseClient.ts
// This initializes the client Firebase app using Secrets.
// IMPORTANT: use process.env.* values (Lovable Secrets will inject env vars).
export const CLIENT_FIREBASE_INIT = `// src/lib/firebaseClient.ts
import { initializeApp, getApps } from 'firebase/app';
import { getAuth } from 'firebase/auth';
// // Firebase config is read from environment variables injected from Lovable Secrets
const firebaseConfig = {
apiKey: process.env.FIREBASE_API_KEY,
authDomain: process.env.FIREBASE_AUTH_DOMAIN,
projectId: process.env.FIREBASE_PROJECT_ID,
appId: process.env.FIREBASE_APP_ID,
};
if (!getApps().length) {
initializeApp(firebaseConfig);
}
export const auth = getAuth();
`;
// Create src/contexts/AuthContext.tsx
export const AUTH_CONTEXT = `// src/contexts/AuthContext.tsx
import React, { createContext, useContext, useEffect, useState } from 'react';
import { onAuthStateChanged, signInWithPopup, GoogleAuthProvider, signOut } from 'firebase/auth';
import { auth } from '../lib/firebaseClient';
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsub = onAuthStateChanged(auth, async (u) => {
setUser(u);
setLoading(false);
});
return () => unsub();
}, []);
const signInWithGoogle = async () => {
const provider = new GoogleAuthProvider();
await signInWithPopup(auth, provider);
};
const signOutUser = async () => {
await signOut(auth);
};
return (
<AuthContext.Provider value={{ user, loading, signInWithGoogle, signOutUser }}>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => useContext(AuthContext);
`;
// Create src/pages/Login.tsx
export const LOGIN_PAGE = `// src/pages/Login.tsx
import React from 'react';
import { useAuth } from '../contexts/AuthContext';
export default function LoginPage() {
const { signInWithGoogle, loading } = useAuth();
if (loading) return <div>Loading...</div>;
return (
<div>
<h1>Sign in</h1>
<button onClick={signInWithGoogle}>Sign in with Google</button>
</div>
);
}
`;
// Update src/App.tsx to wrap routes with AuthProvider (replace <App/> root as needed)
export const APP_UPDATE = `// src/App.tsx
// Update this file: wrap your app with AuthProvider so authentication state is available.
import React from 'react';
import { AuthProvider } from './contexts/AuthContext';
import Routes from './Routes'; // // adjust if your routes live elsewhere
export default function App() {
return (
<AuthProvider>
<Routes />
</AuthProvider>
);
}
`;
// Instruction to Lovable: create files and update App.tsx exactly as above.
Please create the three new files and update src/App.tsx with the provided content. Use the existing routing file name if different but ensure AuthProvider wraps the app root.
Paste this into Lovable chat to instruct the Secrets UI actions.
// Please add these Secrets in Lovable Cloud > Secrets (exact keys below).
// Insert your Firebase web app values into the Secrets UI fields.
Please add:
- FIREBASE_API_KEY = <your Firebase web apiKey>
- FIREBASE_AUTH_DOMAIN = <your Firebase authDomain>
- FIREBASE_PROJECT_ID = <your Firebase projectId>
- FIREBASE_APP_ID = <your Firebase appId>
Also add a service account JSON for server verification:
- FIREBASE_SERVICE_ACCOUNT = <full JSON string of service account key>
Mark secrets as protected/secret.
Paste into Lovable chat. Note: installing firebase-admin must be done via GitHub sync/local install; I mark that step.
// Create a server API route to verify Firebase ID tokens for protected server endpoints.
// Create file: src/pages/api/auth/verifyToken.ts (or /api/auth/verify.ts depending on your framework)
export const VERIFY_API = `// src/pages/api/auth/verifyToken.ts
// // Serverless API that verifies Firebase ID token from Authorization header
import type { NextApiRequest, NextApiResponse } from 'next';
import admin from 'firebase-admin';
// Initialize admin using service account from env (SERVICE_ACCOUNT is JSON string)
if (!admin.apps.length) {
const svc = JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT || '{}');
admin.initializeApp({
credential: admin.credential.cert(svc),
});
}
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const authHeader = req.headers.authorization || '';
const token = authHeader.replace('Bearer ', '');
if (!token) return res.status(401).json({ error: 'Missing token' });
try {
const decoded = await admin.auth().verifyIdToken(token);
return res.status(200).json({ uid: decoded.uid, decoded });
} catch (err) {
return res.status(401).json({ error: 'Invalid token' });
}
}
`;
// Create the file with the content above.
Please create src/pages/api/auth/verifyToken.ts with that content.
Use Lovable Secrets for any sensitive values, keep only public keys in client code, centralize Firebase client init (read env vars), add a single AuthProvider + useAuth hook that listens to onIdTokenChanged and periodically refreshes the token, protect routes with a requireAuth wrapper, and never commit service-account secrets — for any server-side ID-token verification use a server-only endpoint (implement in repo via Lovable edits) but export to GitHub and deploy outside Lovable if you need to run firebase-admin or install server dependencies.
// Paste this into Lovable chat to create a secure client init and README instructions.
// Create file src/lib/firebaseClient.ts
// and update README.md with Secrets UI instructions.
Please create a new file at src/lib/firebaseClient.ts with this content:
// initialize Firebase client using environment variables
import { initializeApp, getApps } from "firebase/app";
import { getAuth } from "firebase/auth";
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};
if (!getApps().length) {
initializeApp(firebaseConfig);
}
export const auth = getAuth();
Also update README.md (or create README.md) with a "Secrets" section that instructs:
- Add the client keys above via Lovable's Secrets UI as NEXT_PUBLIC_FIREBASE_API_KEY, NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, etc.
- Do NOT commit service account JSON. If you need server verification, store service account JSON in Lovable Secrets as FIREBASE_ADMIN_SA (JSON) and follow the separate server deployment note.
// Paste this into Lovable chat to add an AuthProvider and hook.
// Create src/hooks/useAuth.tsx and src/components/AuthProvider.tsx
// Update src/pages/_app.tsx (or src/App.tsx) to wrap the app with <AuthProvider>.
Please create src/hooks/useAuth.tsx:
import React, { useEffect, useState, useContext, createContext } from "react";
import { onIdTokenChanged, User } from "firebase/auth";
import { auth } from "../lib/firebaseClient";
type AuthContextValue = { user: User | null; token: string | null; loading: boolean };
const AuthContext = createContext<AuthContextValue>({ user: null, token: null, loading: true });
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [token, setToken] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let refreshInterval: number | undefined;
const unsubscribe = onIdTokenChanged(auth, async (u) => {
setUser(u ?? null);
if (u) {
const t = await u.getIdToken();
setToken(t);
// refresh token every 45 minutes while logged in
refreshInterval = window.setInterval(async () => {
const fresh = await u.getIdToken(true);
setToken(fresh);
}, 45 * 60 * 1000);
} else {
setToken(null);
if (refreshInterval) clearInterval(refreshInterval);
}
setLoading(false);
});
return () => {
unsubscribe();
if (refreshInterval) clearInterval(refreshInterval);
};
}, []);
return <AuthContext.Provider value={{ user, token, loading }}>{children}</AuthContext.Provider>;
}
Then update src/pages/_app.tsx (or src/App.tsx) to wrap the app:
// inside _app.tsx or App.tsx
import { AuthProvider } from "../hooks/useAuth";
function MyApp({ Component, pageProps }) {
return (
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
);
}
export default MyApp;
// Paste this into Lovable chat to add a route protection helper.
// Create src/lib/requireAuth.tsx and show how to use it in a page file.
Please create src/lib/requireAuth.tsx:
import React from "react";
import { useRouter } from "next/router";
import { useAuth } from "../hooks/useAuth";
export function RequireAuth({ children }: { children: React.ReactNode }) {
const { user, loading } = useAuth();
const router = useRouter();
React.useEffect(() => {
if (!loading && !user) {
router.replace("/login");
}
}, [user, loading]);
if (loading || !user) return <div>Loading...</div>;
return <>{children}</>;
}
Usage example for a page (edit pages/protected.tsx):
// wrap page export default function ProtectedPage() { return (<RequireAuth>...page...</RequireAuth>) }
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.