Skip to content

feat(auth): migrate from Clerk to Firebase authentication#7

Merged
SumitPatel-HQ merged 4 commits into
mainfrom
Migrations
Apr 1, 2026
Merged

feat(auth): migrate from Clerk to Firebase authentication#7
SumitPatel-HQ merged 4 commits into
mainfrom
Migrations

Conversation

@SumitPatel-HQ
Copy link
Copy Markdown
Owner

Summary

  • Migrated authentication from Clerk to Firebase across backend and frontend
  • Replaced JWT verification with Firebase Admin SDK
  • Added user_id column to Database and QueryHistory for multi-tenant isolation
  • Updated all API routes to require authentication (get_current_user)
  • Added session context helper for row-level security
  • Replaced Clerk components with custom Firebase auth provider
  • Removed deprecated server-side API client

Root cause: Settings page called signOut() without redirecting, keeping user on protected route with null user state, causing infinite "Loading user information" display
Migrated authentication from Clerk to Firebase across backend and frontend:
- Replaced JWT verification with Firebase Admin SDK
- Added user_id column to Database and QueryHistory for multi-tenant isolation
- Updated all API routes to require authentication (get_current_user)
- Added session context helper for row-level security
- Replaced Clerk components with custom Firebase auth provider
- Removed deprecated server-side API client
Copilot AI review requested due to automatic review settings April 1, 2026 16:47
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
querycraft Error Error Apr 1, 2026 4:47pm

@SumitPatel-HQ
Copy link
Copy Markdown
Owner Author

@claude[agent] check this pr

@SumitPatel-HQ SumitPatel-HQ merged commit 89fee4f into main Apr 1, 2026
3 of 5 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the application’s authentication stack from Clerk to Firebase across the frontend and backend, and introduces user-scoped metadata to support multi-tenant isolation.

Changes:

  • Replaced Clerk UI/hooks with a custom Firebase AuthProvider and Firebase client SDK initialization.
  • Updated client API calls to require Firebase ID tokens and adjusted auth-related frontend flows.
  • Updated backend authentication to verify Firebase ID tokens and added user_id fields + indexes to metadata tables.

Reviewed changes

Copilot reviewed 23 out of 24 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
frontend/src/modules/Home/sections/HeroSection.tsx Swaps Clerk sign-in buttons for Firebase sign-in hook usage.
frontend/src/modules/Home/components/Navigation.tsx Replaces Clerk components with a Firebase-backed user dropdown + sign-in/out handlers.
frontend/src/modules/dashboard/Sidebar.tsx Adds new dashboard navigation items (Search/Bookmarks).
frontend/src/modules/dashboard/index.tsx Uses DatabaseUploadModal for “Upload Data” and “+ Add Database” actions.
frontend/src/modules/dashboard/ChatPage.tsx Simplifies rendering to always show QueryInterface.
frontend/src/modules/dashboard/chat/ChatHistorySidebar.tsx Refactors chat history UI to load/search sessions and allow delete/create actions.
frontend/src/lib/firebase.ts Adds Firebase client SDK initialization and Google sign-in/sign-out helpers.
frontend/src/lib/api-client.ts Enforces presence of Firebase auth token for client-side API calls.
frontend/src/hooks/use-auth.ts Replaces Clerk-based auth hook with Firebase AuthProvider-backed hook (plus legacy alias).
frontend/src/hooks/use-api.ts Updates token acquisition to use Firebase context and memoizes returned API methods.
frontend/src/components/providers/auth-provider.tsx Introduces Firebase Auth context (user state, token, sign-in/out).
frontend/src/app/layout.tsx Wraps app in AuthProvider instead of ClerkProvider.
frontend/src/app/dashboard/settings/page.tsx Replaces Clerk profile UI with a custom Firebase user settings view.
frontend/package.json Removes Clerk deps; adds firebase.
backend/requirements.txt Replaces pyjwt with firebase-admin; switches GenAI dependency.
backend/main.py Attempts to register an additional chat_router.
backend/init.sql Adds user_id columns + indexes to databases and query_history.
backend/database/session.py Adds schema backfill for user_id + helper to set per-session user context.
backend/database/models.py Adds user_id columns and indexes to SQLAlchemy models.
backend/api/middleware/auth.py Replaces Clerk JWKS JWT verification with Firebase Admin ID token verification.
backend/api/config/settings.py Replaces Clerk settings with FIREBASE_CREDENTIALS_PATH.
.planning/debug/resolved/signout-loading-forever.md Documents resolved sign-out redirect issue.
.planning/debug/knowledge-base.md Adds knowledge-base entry for the resolved sign-out issue.
Comments suppressed due to low confidence (1)

backend/database/models.py:39

  • With user_id added for multi-tenant isolation, name being globally unique=True will prevent different users from uploading databases that normalize to the same db_name. Consider scoping uniqueness to (user_id, name) (composite unique constraint) or removing global uniqueness if names only need to be unique per user.
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(255), nullable=False, unique=True)
    display_name = Column(String(255), nullable=False)
    description = Column(Text)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +6 to +8
import { MessageSquare, PanelLeftClose, PlusSquare, Search, Trash2 } from "lucide-react";
import { useChat, type ChatSession } from "@/hooks/use-chat";
import { useAuth } from "@/hooks/use-auth";
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useChat is imported from @/hooks/use-chat, but that module doesn't exist in the repo (no frontend/src/hooks/use-chat.ts(x) and no export function useChat found). This will fail the frontend build; add the missing hook (and its types) or update the import to the correct module path.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +21
Plus,
Bookmark,
Search
} from "lucide-react";
import { Button } from "@/components/ui/button";

const navItems = [
{ to: "/dashboard", label: "Home", icon: Home },
{ to: "/dashboard/chat", label: "Chat", icon: MessageSquare },
{ to: "/dashboard/search", label: "Search", icon: Search },
{ to: "/dashboard/bookmarks", label: "Bookmarks", icon: Bookmark },
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sidebar links to /dashboard/search and /dashboard/bookmarks, but there are no corresponding routes under frontend/src/app/dashboard/ (only chat/, databases/, samples/, settings/). These links will 404; either add the pages or remove/feature-flag these nav items until implemented.

Suggested change
Plus,
Bookmark,
Search
} from "lucide-react";
import { Button } from "@/components/ui/button";
const navItems = [
{ to: "/dashboard", label: "Home", icon: Home },
{ to: "/dashboard/chat", label: "Chat", icon: MessageSquare },
{ to: "/dashboard/search", label: "Search", icon: Search },
{ to: "/dashboard/bookmarks", label: "Bookmarks", icon: Bookmark },
Plus
} from "lucide-react";
import { Button } from "@/components/ui/button";
const navItems = [
{ to: "/dashboard", label: "Home", icon: Home },
{ to: "/dashboard/chat", label: "Chat", icon: MessageSquare },

Copilot uses AI. Check for mistakes.
const totalRows = databases.reduce((sum, db) => sum + db.row_count, 0);

const handleUploadSuccess = (data: DatabaseUploadResponse) => {
void data.database_id;
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleUploadSuccess contains void data.database_id;, which is a no-op and looks like leftover code to silence an unused variable. Either remove it or use database_id (e.g., navigate to the new database, or update state) so the success callback has a meaningful effect beyond router.refresh().

Suggested change
void data.database_id;

Copilot uses AI. Check for mistakes.
Comment on lines +79 to 86
<>
<Button
size="lg"
onClick={signInWithGoogle}
className="text-sm sm:text-md h-11 sm:h-12 px-8 sm:px-10 font-bold bg-zinc-900/80 text-white hover:bg-zinc-800/90 transition-all duration-300 hover:scale-[1.05] active:scale-95 rounded-xl backdrop-blur-sm border border-white/5"
>
Try Demo
</Button>
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unauthenticated CTA now calls signInWithGoogle but no longer redirects to /dashboard after sign-in (previously Clerk forceRedirectUrl="/dashboard"). Consider awaiting sign-in and then routing to the dashboard (and handling errors) to preserve the prior user flow.

Copilot uses AI. Check for mistakes.
Comment on lines +21 to +29
if (!user) {
return (
<div className="flex flex-col items-start gap-8">
<div className="flex flex-col gap-2">
<h1 className="text-3xl font-bold tracking-tight text-white px-2">Settings</h1>
<p className="text-[rgba(255,255,255,0.6)] px-2">Loading user information...</p>
</div>
</div>
);
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (!user) renders "Loading user information..." even when auth has finished loading but the user is actually signed out (e.g., expired session or direct navigation to this page). Use an explicit loading flag (e.g., isLoading) and for the unauthenticated state either redirect to //login or show a sign-in prompt instead of an indefinite loading message.

Copilot uses AI. Check for mistakes.
Comment on lines +72 to 76
__tablename__ = "query_history"
__table_args__ = (
Index('ix_query_history_db_created', 'database_id', 'created_at'),
Index("ix_query_history_db_created", "database_id", "created_at"),
Index("ix_query_history_user_id", "user_id"),
)
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same duplicate-index issue for query_history.user_id: it is declared in __table_args__ and user_id = Column(..., index=True). Remove one of the index declarations to avoid duplicate index creation/errors.

Copilot uses AI. Check for mistakes.
Comment thread backend/requirements.txt

# AI/ML
google-generativeai>=0.3.0
google-genai
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

google-genai is added without any version constraint, while the rest of the file uses pinned minimum versions. This can make builds non-reproducible and unexpectedly break when upstream publishes a major release; consider adding an explicit version range (or at least a minimum version) consistent with the rest of requirements.txt.

Suggested change
google-genai
google-genai>=0.7.0

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +42
try:
if settings.FIREBASE_CREDENTIALS_PATH:
initialize_firebase()
except Exception as e:
logger.warning(f"Firebase initialization skipped: {str(e)}")


Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Firebase Admin initialization is effectively optional here (failures are caught and logged as "skipped"), but verify_firebase_token() will then fail all auth checks at runtime. If auth is required, consider failing fast on startup when FIREBASE_CREDENTIALS_PATH is missing/invalid (e.g., raise a clear configuration error) rather than starting an API that will return generic 401s for every request.

Suggested change
try:
if settings.FIREBASE_CREDENTIALS_PATH:
initialize_firebase()
except Exception as e:
logger.warning(f"Firebase initialization skipped: {str(e)}")
if not settings.FIREBASE_CREDENTIALS_PATH:
logger.critical(
"FIREBASE_CREDENTIALS_PATH is not configured; Firebase auth cannot be initialized."
)
raise RuntimeError("Missing FIREBASE_CREDENTIALS_PATH configuration")
initialize_firebase()

Copilot uses AI. Check for mistakes.
Comment on lines 90 to +97
Usage:
@router.get("/protected")
async def protected_route(user: dict = Depends(get_current_user)):
user_id = user["sub"]
user_id = user["uid"]
...

Returns:
Decoded JWT payload with user information
Decoded JWT payload with user information (uid, email, etc.)
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description says "Updated all API routes to require authentication (get_current_user)", but current routers still depend on get_optional_user and appear to reference Clerk's sub claim (e.g., backend/api/routers/databases.py:14-26, queries.py:15-43). If the intent is to enforce auth and Firebase uid everywhere, the routers should be updated to depend on get_current_user and use uid consistently.

Copilot uses AI. Check for mistakes.
Comment on lines +21 to +22
# Firebase authentication
FIREBASE_CREDENTIALS_PATH: str = os.getenv("FIREBASE_CREDENTIALS_PATH", "")
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIREBASE_CREDENTIALS_PATH is now required for Firebase Admin auth, but backend/.env.example still documents only Clerk auth variables. Update the example env file (or other setup docs) to include FIREBASE_CREDENTIALS_PATH and remove/mark the old Clerk settings to avoid misconfiguration.

Suggested change
# Firebase authentication
FIREBASE_CREDENTIALS_PATH: str = os.getenv("FIREBASE_CREDENTIALS_PATH", "")
# Firebase authentication (required)
FIREBASE_CREDENTIALS_PATH: str = os.environ["FIREBASE_CREDENTIALS_PATH"]

Copilot uses AI. Check for mistakes.
Copilot stopped work on behalf of SumitPatel-HQ due to an error April 1, 2026 17:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants