import { createContext, ReactNode, useContext, useEffect, useState } from "react"; import { useQuery, useMutation, UseMutationResult, } from "@tanstack/react-query"; import { insertUserSchema, User as SelectUser, InsertUser } from "@shared/schema"; import { getQueryFn, apiRequest, queryClient } from "../lib/queryClient"; import { useToast } from "@/hooks/use-toast"; type AuthContextType = { user: SelectUser | null; isLoading: boolean; error: Error | null; loginMutation: UseMutationResult; ldapLoginMutation: UseMutationResult; initiateOidcLogin: () => void; logoutMutation: UseMutationResult; registerMutation: UseMutationResult; ldapEnabled: boolean; oidcEnabled: boolean; }; type LoginData = Pick; type LdapLoginData = Pick; export const AuthContext = createContext(null); export function AuthProvider({ children }: { children: ReactNode }) { const { toast } = useToast(); const [ldapEnabled, setLdapEnabled] = useState(false); const [oidcEnabled, setOidcEnabled] = useState(false); const { data: user, error, isLoading, } = useQuery({ queryKey: ["/api/user"], queryFn: getQueryFn({ on401: "returnNull" }), }); // Get auth provider configurations useEffect(() => { const fetchAuthProviders = async () => { try { const response = await fetch('/api/auth/providers'); if (response.ok) { const providers = await response.json(); setLdapEnabled(providers.ldap?.enabled || false); setOidcEnabled(providers.oidc?.enabled || false); } } catch (err) { // Silently fail - default is disabled console.error('Failed to fetch auth providers:', err); } }; fetchAuthProviders(); }, []); const loginMutation = useMutation({ mutationFn: async (credentials: LoginData) => { const res = await apiRequest("POST", "/api/login", credentials); return await res.json(); }, onSuccess: (user: SelectUser) => { queryClient.setQueryData(["/api/user"], user); toast({ title: "Login successful", description: `Welcome back, ${user.username}!`, }); }, onError: (error: Error) => { toast({ title: "Login failed", description: error.message, variant: "destructive", }); }, }); const registerMutation = useMutation({ mutationFn: async (credentials: InsertUser) => { const res = await apiRequest("POST", "/api/register", credentials); return await res.json(); }, onSuccess: (user: SelectUser) => { queryClient.setQueryData(["/api/user"], user); toast({ title: "Registration successful", description: `Welcome, ${user.username}!`, }); }, onError: (error: Error) => { toast({ title: "Registration failed", description: error.message, variant: "destructive", }); }, }); const ldapLoginMutation = useMutation({ mutationFn: async (credentials: LdapLoginData) => { const res = await apiRequest("POST", "/api/auth/ldap", credentials); return await res.json(); }, onSuccess: (user: SelectUser) => { queryClient.setQueryData(["/api/user"], user); toast({ title: "LDAP Login successful", description: `Welcome back, ${user.username}!`, }); }, onError: (error: Error) => { toast({ title: "LDAP Login failed", description: error.message, variant: "destructive", }); }, }); // Function to initiate OIDC login flow const initiateOidcLogin = () => { // OpenID Connect requires a redirect to the provider's login page window.location.href = "/api/auth/oidc"; }; const logoutMutation = useMutation({ mutationFn: async () => { await apiRequest("POST", "/api/logout"); }, onSuccess: () => { queryClient.setQueryData(["/api/user"], null); toast({ title: "Logged out", description: "You have been successfully logged out.", }); }, onError: (error: Error) => { toast({ title: "Logout failed", description: error.message, variant: "destructive", }); }, }); const authContextValue: AuthContextType = { user: user ?? null, isLoading, error, loginMutation, ldapLoginMutation, initiateOidcLogin, logoutMutation, registerMutation, ldapEnabled, oidcEnabled, }; return ( {children} ); } export function useAuth() { const context = useContext(AuthContext); if (!context) { throw new Error("useAuth must be used within an AuthProvider"); } return context; }