From b2b240c16a645d9c24199545ac9b0db6cf5540bf Mon Sep 17 00:00:00 2001 From: Aljaz Ceru Date: Thu, 18 Sep 2025 08:32:08 +0200 Subject: [PATCH] auth consolidation --- frontend/src/app/dashboard/page.tsx | 2 +- frontend/src/app/login/page.tsx | 2 +- frontend/src/app/prompt-templates/page.tsx | 2 +- frontend/src/app/rag/page.tsx | 2 +- frontend/src/app/test-auth/page.tsx | 2 +- .../src/components/plugins/PluginManager.tsx | 2 +- .../components/plugins/PluginPageRenderer.tsx | 10 +- .../components/providers/auth-provider.tsx | 3 + frontend/src/contexts/AuthContext.tsx | 194 ------------------ frontend/src/contexts/PluginContext.tsx | 2 +- 10 files changed, 16 insertions(+), 205 deletions(-) delete mode 100644 frontend/src/contexts/AuthContext.tsx diff --git a/frontend/src/app/dashboard/page.tsx b/frontend/src/app/dashboard/page.tsx index 35a7db4..30ee0f1 100644 --- a/frontend/src/app/dashboard/page.tsx +++ b/frontend/src/app/dashboard/page.tsx @@ -1,6 +1,6 @@ "use client" -import { useAuth } from "@/contexts/AuthContext" +import { useAuth } from "@/components/providers/auth-provider" import { useState, useEffect } from "react" import { ProtectedRoute } from "@/components/auth/ProtectedRoute" import { useToast } from "@/hooks/use-toast" diff --git a/frontend/src/app/login/page.tsx b/frontend/src/app/login/page.tsx index 027c45e..ffdeebc 100644 --- a/frontend/src/app/login/page.tsx +++ b/frontend/src/app/login/page.tsx @@ -2,7 +2,7 @@ import { useState } from "react" import { useRouter } from "next/navigation" -import { useAuth } from "@/contexts/AuthContext" +import { useAuth } from "@/components/providers/auth-provider" // Force dynamic rendering for authentication export const dynamic = 'force-dynamic' diff --git a/frontend/src/app/prompt-templates/page.tsx b/frontend/src/app/prompt-templates/page.tsx index 57f438d..63a4d8b 100644 --- a/frontend/src/app/prompt-templates/page.tsx +++ b/frontend/src/app/prompt-templates/page.tsx @@ -31,7 +31,7 @@ import { Edit3, RotateCcw, Loader2, Save, AlertTriangle, Plus, Sparkles } from ' import toast from 'react-hot-toast' import { apiClient } from '@/lib/api-client' import { config } from '@/lib/config' -import { useAuth } from '@/contexts/AuthContext' +import { useAuth } from '@/components/providers/auth-provider' interface PromptTemplate { id: string diff --git a/frontend/src/app/rag/page.tsx b/frontend/src/app/rag/page.tsx index d69732f..1286f39 100644 --- a/frontend/src/app/rag/page.tsx +++ b/frontend/src/app/rag/page.tsx @@ -11,7 +11,7 @@ import { Plus, Database, Upload, Search, Trash2, FileText, AlertCircle } from "l import { CollectionManager } from "@/components/rag/collection-manager" import { DocumentUpload } from "@/components/rag/document-upload" import { DocumentBrowser } from "@/components/rag/document-browser" -import { useAuth } from "@/contexts/AuthContext" +import { useAuth } from "@/components/providers/auth-provider" import { ProtectedRoute } from '@/components/auth/ProtectedRoute' import { apiClient } from '@/lib/api-client' diff --git a/frontend/src/app/test-auth/page.tsx b/frontend/src/app/test-auth/page.tsx index 592ab52..db4c3c0 100644 --- a/frontend/src/app/test-auth/page.tsx +++ b/frontend/src/app/test-auth/page.tsx @@ -1,6 +1,6 @@ "use client" -import { useAuth } from "@/contexts/AuthContext" +import { useAuth } from "@/components/providers/auth-provider" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { useState } from "react" diff --git a/frontend/src/components/plugins/PluginManager.tsx b/frontend/src/components/plugins/PluginManager.tsx index 9a58e10..dbbf1f1 100644 --- a/frontend/src/components/plugins/PluginManager.tsx +++ b/frontend/src/components/plugins/PluginManager.tsx @@ -28,7 +28,7 @@ import { AlertCircle } from 'lucide-react'; import { usePlugin, type PluginInfo, type AvailablePlugin } from '../../contexts/PluginContext'; -import { useAuth } from '../../contexts/AuthContext'; +import { useAuth } from '@/components/providers/auth-provider'; import { PluginConfigurationDialog } from './PluginConfigurationDialog'; interface PluginCardProps { diff --git a/frontend/src/components/plugins/PluginPageRenderer.tsx b/frontend/src/components/plugins/PluginPageRenderer.tsx index dedd3cb..ce2d0d8 100644 --- a/frontend/src/components/plugins/PluginPageRenderer.tsx +++ b/frontend/src/components/plugins/PluginPageRenderer.tsx @@ -8,7 +8,8 @@ import { Alert, AlertDescription } from '@/components/ui/alert'; import { Card, CardContent } from '@/components/ui/card'; import { Skeleton } from '@/components/ui/skeleton'; import { AlertCircle, Loader2 } from 'lucide-react'; -import { useAuth } from '../../contexts/AuthContext'; +import { useAuth } from '@/components/providers/auth-provider'; +import { tokenManager } from '@/lib/token-manager'; import { usePlugin, type PluginInfo } from '../../contexts/PluginContext'; import { config } from '../../lib/config'; @@ -48,8 +49,8 @@ const PluginIframe: React.FC = ({ // Validate origin - should be from our backend const allowedOrigins = [ window.location.origin, - config.getBackendUrl(), - config.getApiUrl() + config.API_BASE_URL, + config.API_BASE_URL ].filter(Boolean); if (!allowedOrigins.some(origin => event.origin.startsWith(origin))) { @@ -161,7 +162,8 @@ export const PluginPageRenderer: React.FC = ({ pagePath, componentName }) => { - const { user, token } = useAuth(); + const { user } = useAuth(); + const token = tokenManager.getAccessToken(); const { installedPlugins, getPluginPages, diff --git a/frontend/src/components/providers/auth-provider.tsx b/frontend/src/components/providers/auth-provider.tsx index 81b952c..97c2414 100644 --- a/frontend/src/components/providers/auth-provider.tsx +++ b/frontend/src/components/providers/auth-provider.tsx @@ -9,6 +9,7 @@ interface User { id: string username: string email: string + name?: string role: string permissions: string[] created_at: string @@ -18,6 +19,7 @@ interface User { interface AuthContextType { user: User | null isLoading: boolean + isAuthenticated: boolean login: (username: string, password: string) => Promise logout: () => void register: (username: string, email: string, password: string) => Promise @@ -114,6 +116,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const value: AuthContextType = { user, isLoading, + isAuthenticated: !!user, login, logout, register, diff --git a/frontend/src/contexts/AuthContext.tsx b/frontend/src/contexts/AuthContext.tsx deleted file mode 100644 index 20f5c48..0000000 --- a/frontend/src/contexts/AuthContext.tsx +++ /dev/null @@ -1,194 +0,0 @@ -"use client" - -import { createContext, useContext, useState, useEffect, ReactNode } from "react" -import { useRouter } from "next/navigation" -import { tokenManager } from "@/lib/token-manager" - -// Helper function to get API URL with proper protocol -const getApiUrl = () => { - if (typeof window !== 'undefined') { - const protocol = window.location.protocol.slice(0, -1) // Remove ':' from 'https:' - const host = window.location.hostname - return `${protocol}://${host}` - } - return `http://${process.env.NEXT_PUBLIC_BASE_URL || 'localhost'}` -} - -interface User { - id: string - email: string - name: string - role: string -} - -interface AuthContextType { - user: User | null - isAuthenticated: boolean - login: (email: string, password: string) => Promise - logout: () => void - isLoading: boolean -} - -const AuthContext = createContext(undefined) - -export function AuthProvider({ children }: { children: ReactNode }) { - const [user, setUser] = useState(null) - const [isLoading, setIsLoading] = useState(true) - const router = useRouter() - - // Initialize auth state and listen to token manager events - useEffect(() => { - const initAuth = async () => { - // Check if we have valid tokens - if (tokenManager.isAuthenticated()) { - // Try to get user info - await fetchUserInfo() - } - setIsLoading(false) - } - - // Set up event listeners - const handleTokensUpdated = () => { - // Tokens were updated (refreshed), update user if needed - if (!user) { - fetchUserInfo() - } - } - - const handleTokensCleared = () => { - // Tokens were cleared, clear user - setUser(null) - } - - const handleSessionExpired = (reason: string) => { - console.log('Session expired:', reason) - setUser(null) - // TokenManager and API client will handle redirect - } - - const handleLogout = () => { - setUser(null) - router.push('/login') - } - - // Register event listeners - tokenManager.on('tokensUpdated', handleTokensUpdated) - tokenManager.on('tokensCleared', handleTokensCleared) - tokenManager.on('sessionExpired', handleSessionExpired) - tokenManager.on('logout', handleLogout) - - // Initialize - initAuth() - - // Cleanup - return () => { - tokenManager.off('tokensUpdated', handleTokensUpdated) - tokenManager.off('tokensCleared', handleTokensCleared) - tokenManager.off('sessionExpired', handleSessionExpired) - tokenManager.off('logout', handleLogout) - } - }, []) - - const fetchUserInfo = async () => { - try { - const token = await tokenManager.getAccessToken() - if (!token) return - - const response = await fetch(`${getApiUrl()}/api-internal/v1/auth/me`, { - headers: { - 'Authorization': `Bearer ${token}`, - }, - }) - - if (response.ok) { - const userData = await response.json() - const user = { - id: userData.id || userData.sub, - email: userData.email, - name: userData.name || userData.email, - role: userData.role || 'user', - } - setUser(user) - - // Store user info for offline access - if (typeof window !== 'undefined') { - localStorage.setItem('user', JSON.stringify(user)) - } - } else if (response.status === 401) { - // Token is invalid or expired, clear it - console.log('Token invalid, clearing tokens') - tokenManager.clearTokens() - setUser(null) - } - } catch (error) { - console.error('Failed to fetch user info:', error) - // If there's an error, clear tokens to be safe - tokenManager.clearTokens() - setUser(null) - } - } - - const login = async (email: string, password: string) => { - setIsLoading(true) - - try { - const response = await fetch(`${getApiUrl()}/api-internal/v1/auth/login`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ email, password }), - }) - - if (!response.ok) { - const error = await response.json() - throw new Error(error.detail || 'Invalid credentials') - } - - const data = await response.json() - - // Store tokens in TokenManager - tokenManager.setTokens(data.access_token, data.refresh_token) - - // Fetch user info - await fetchUserInfo() - - // Navigate to dashboard - router.push('/dashboard') - - } catch (error) { - console.error('Login error:', error) - throw error - } finally { - setIsLoading(false) - } - } - - const logout = () => { - tokenManager.clearTokens() - setUser(null) - router.push('/login') - } - - return ( - - {children} - - ) -} - -export function useAuth() { - const context = useContext(AuthContext) - if (!context) { - throw new Error("useAuth must be used within an AuthProvider") - } - return context -} \ No newline at end of file diff --git a/frontend/src/contexts/PluginContext.tsx b/frontend/src/contexts/PluginContext.tsx index 217f70b..03de119 100644 --- a/frontend/src/contexts/PluginContext.tsx +++ b/frontend/src/contexts/PluginContext.tsx @@ -4,7 +4,7 @@ * Plugin Context - Manages plugin state and UI integration */ import React, { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react'; -import { useAuth } from './AuthContext'; +import { useAuth } from '@/components/providers/auth-provider'; import { apiClient } from '@/lib/api-client'; export interface PluginInfo {