=> {
+ const response = await fetch('/api/setup-convex-auth', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ projectId }),
+ })
+
+ if (!response.ok) {
+ const error = await response.json()
+ throw new Error(error.error || 'Failed to setup Convex Auth')
+ }
+ }, [projectId])
+
// Legacy compatibility
const isDesktopMode = viewMode === 'desktop'
@@ -2428,6 +2442,7 @@ function ProjectPageInternal() {
cloudEnabled={cloudEnabled}
cloudDeploymentUrl={cloudDeploymentUrl}
onCloudEnabled={handleCloudEnabled}
+ onSetupAuth={handleSetupAuth}
>
{/* Fixed width Chat/History panel - 500px */}
@@ -2610,6 +2625,22 @@ function ProjectPageInternal() {
cloudEnabled={cloudEnabled}
deploymentUrl={cloudDeploymentUrl}
onCloudEnabled={handleCloudEnabled}
+ onNavigateToAuth={() => setMobileSidebarPanel('auth')}
+ onClose={() => setMobileSidebarPanel(null)}
+ />
+
+
+
+
!open && setMobileSidebarPanel(null)}>
+
+
+ Authentication
+
+ setMobileSidebarPanel('cloud')}
+ onSetupAuth={handleSetupAuth}
onClose={() => setMobileSidebarPanel(null)}
/>
diff --git a/apps/web/components/app-sidebar.tsx b/apps/web/components/app-sidebar.tsx
index 2ebd8d03..9c01a402 100644
--- a/apps/web/components/app-sidebar.tsx
+++ b/apps/web/components/app-sidebar.tsx
@@ -1,7 +1,7 @@
"use client"
import { useState, useEffect, useRef } from "react"
-import { Image, FolderOpen, Database, ChevronLeft, ChevronRight, X, MessageSquare, Cloud } from "lucide-react"
+import { Image, FolderOpen, Database, ChevronLeft, ChevronRight, X, MessageSquare, Cloud, KeyRound } from "lucide-react"
import {
Sidebar,
SidebarContent,
@@ -19,6 +19,7 @@ import { AssetsPanel } from "@/components/assets-panel"
import { ProjectsPanel } from "@/components/projects-panel"
import { BackendPanel } from "@/components/backend-panel"
import { CloudSidebarPanel } from "@/components/cloud-sidebar-panel"
+import { AuthSidebarPanel } from "@/components/auth-sidebar-panel"
import { UserMenu } from "@/components/user-menu"
import { Session } from "@/lib/auth"
import { cn } from "@/lib/utils"
@@ -39,6 +40,7 @@ interface AppSidebarProps {
cloudEnabled?: boolean
cloudDeploymentUrl?: string
onCloudEnabled?: () => void
+ onSetupAuth?: () => Promise
}
function SidebarToggle() {
@@ -93,6 +95,12 @@ function SidebarNav({
icon: Cloud,
spacer: false,
},
+ {
+ id: "auth",
+ label: "Authentication",
+ icon: KeyRound,
+ spacer: false,
+ },
{
id: "backend",
label: "Backend",
@@ -182,7 +190,7 @@ function PanelContent({
children,
onClose,
}: {
- isBottomOption: boolean
+ isBottomOption?: boolean
isOpen: boolean
isFirstOpen: boolean
isSwitching: boolean
@@ -296,6 +304,7 @@ export function AppSidebar({
cloudEnabled,
cloudDeploymentUrl,
onCloudEnabled,
+ onSetupAuth,
}: AppSidebarProps) {
const [internalActivePanel, setInternalActivePanel] = useState(null)
const [isFirstOpen, setIsFirstOpen] = useState(true)
@@ -396,6 +405,23 @@ export function AppSidebar({
cloudEnabled={cloudEnabled || false}
deploymentUrl={cloudDeploymentUrl}
onCloudEnabled={onCloudEnabled}
+ onNavigateToAuth={() => handlePanelChange('auth')}
+ onClose={() => handlePanelChange(null)}
+ />
+
+
+ handlePanelChange(null)}
+ isBottomOption
+ >
+ handlePanelChange('cloud')}
+ onSetupAuth={onSetupAuth || (async () => {})}
onClose={() => handlePanelChange(null)}
/>
diff --git a/apps/web/components/auth-sidebar-panel.tsx b/apps/web/components/auth-sidebar-panel.tsx
new file mode 100644
index 00000000..a46d7c08
--- /dev/null
+++ b/apps/web/components/auth-sidebar-panel.tsx
@@ -0,0 +1,197 @@
+'use client'
+
+import { useState } from 'react'
+import { Button } from '@/components/ui/button'
+import {
+ KeyRound,
+ Cloud,
+ CheckCircle2,
+ Loader2,
+ ShieldCheck,
+ Package,
+ Database,
+ Globe,
+ Smartphone,
+ AlertCircle,
+} from 'lucide-react'
+
+interface AuthSidebarPanelProps {
+ projectId?: string
+ cloudEnabled: boolean
+ onNavigateToCloud: () => void
+ onSetupAuth: () => Promise
+ onClose: () => void
+}
+
+const SETUP_STEPS = [
+ { icon: Package, text: '@convex-dev/auth package installation' },
+ { icon: ShieldCheck, text: 'Password provider configuration' },
+ { icon: Database, text: 'Auth tables in your database schema' },
+ { icon: Globe, text: 'HTTP routes for authentication' },
+ { icon: Smartphone, text: 'ConvexAuthProvider in your app layout' },
+]
+
+type SetupStatus = 'idle' | 'loading' | 'success' | 'error'
+
+export function AuthSidebarPanel({
+ projectId,
+ cloudEnabled,
+ onNavigateToCloud,
+ onSetupAuth,
+ onClose,
+}: AuthSidebarPanelProps) {
+ const [setupStatus, setSetupStatus] = useState('idle')
+ const [errorMessage, setErrorMessage] = useState(null)
+
+ const handleSetupAuth = async () => {
+ setSetupStatus('loading')
+ setErrorMessage(null)
+ try {
+ await onSetupAuth()
+ setSetupStatus('success')
+ } catch (error) {
+ setSetupStatus('error')
+ setErrorMessage(error instanceof Error ? error.message : 'An unexpected error occurred')
+ }
+ }
+
+ return (
+
+ {/* Header */}
+
+
+
+ Authentication
+
+
+
+ {/* Content */}
+
+
+
+ Add user authentication to your app
+
+
+ {!cloudEnabled ? (
+
+
+
+
+
+ Cloud Required
+
+
+ Authentication via Convex requires Cloud to be enabled for your project.
+
+
+
+
+
+ Enable cloud to add Convex Auth to your app. Convex provides secure,
+ production-ready authentication with built-in support for email and password.
+
+
+
+
+ Enable Cloud First
+
+
+ ) : setupStatus === 'success' ? (
+
+
+
+
+
+ Auth setup complete
+
+
+ Convex Auth files have been written and committed to your project.
+
+
+
+
+
+
Next step
+
+ Run npx @convex-dev/auth in your project to generate JWT keys, then set CONVEX_SITE_URL in your Convex dashboard.
+
+
+
+
+ Close
+
+
+ ) : (
+
+
+ Set up email and password authentication for your Expo app using Convex Auth.
+
+
+
+
+ What will be set up
+
+
+ {SETUP_STEPS.map(({ icon: Icon, text }) => (
+
+
+ {text}
+
+ ))}
+
+
+
+ {setupStatus === 'error' && errorMessage && (
+
+
+
+
+ Setup failed
+
+
+ {errorMessage}
+
+
+
+ )}
+
+ {setupStatus === 'loading' && (
+
+
+ Installing packages and writing auth files to your project. This may take a minute...
+
+
+ )}
+
+ {setupStatus === 'idle' && (
+
+
+ Auth files will be written directly to your project and committed via git.
+
+
+ )}
+
+
+ {setupStatus === 'loading' ? (
+ <>
+
+ Setting up...
+ >
+ ) : (
+ <>
+
+ {setupStatus === 'error' ? 'Retry Setup' : 'Setup Authentication'}
+ >
+ )}
+
+
+ )}
+
+
+
+ )
+}
diff --git a/apps/web/components/cloud-sidebar-panel.tsx b/apps/web/components/cloud-sidebar-panel.tsx
index 623b72c4..1b5a468d 100644
--- a/apps/web/components/cloud-sidebar-panel.tsx
+++ b/apps/web/components/cloud-sidebar-panel.tsx
@@ -12,7 +12,7 @@ import {
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog'
-import { Database, Zap, Cloud, HardDrive, CheckCircle2, Loader2, ExternalLink } from 'lucide-react'
+import { Database, Zap, Cloud, HardDrive, CheckCircle2, Loader2, ExternalLink, KeyRound } from 'lucide-react'
import { toast } from 'sonner'
interface CloudSidebarPanelProps {
@@ -20,6 +20,7 @@ interface CloudSidebarPanelProps {
cloudEnabled: boolean
deploymentUrl?: string
onCloudEnabled?: () => void
+ onNavigateToAuth?: () => void
onClose: () => void
}
@@ -28,6 +29,7 @@ export function CloudSidebarPanel({
cloudEnabled,
deploymentUrl,
onCloudEnabled,
+ onNavigateToAuth,
onClose,
}: CloudSidebarPanelProps) {
const [isEnabling, setIsEnabling] = useState(false)
@@ -116,6 +118,25 @@ export function CloudSidebarPanel({
Your app now has access to a real-time database. The AI will use Convex for all data persistence and backend logic.
+
+
+
+
+ Authentication
+
+
+ Add email/password authentication to your app with Convex Auth.
+
+
+
+ Setup Authentication
+
+
) : (
diff --git a/apps/web/components/mobile-burger-menu.tsx b/apps/web/components/mobile-burger-menu.tsx
index d9d5c5c0..3070fd74 100644
--- a/apps/web/components/mobile-burger-menu.tsx
+++ b/apps/web/components/mobile-burger-menu.tsx
@@ -16,6 +16,7 @@ import {
FolderOpen,
Database,
Cloud,
+ KeyRound,
Download,
Rocket,
ExternalLink,
@@ -136,6 +137,7 @@ export function MobileBurgerMenu({
{ id: null, label: "Chat", icon: MessageSquare },
{ id: "assets", label: "Assets", icon: Image },
{ id: "cloud", label: "Cloud", icon: Cloud },
+ { id: "auth", label: "Authentication", icon: KeyRound },
{ id: "projects", label: "Projects", icon: FolderOpen },
{ id: "backend", label: "Backend", icon: Database },
]