openrag/frontend/contexts/auth-context.tsx
Cole Goldsmith d47038e097
Reorganize folders within frontend (#407)
* reorganize folder structure

* move folders from merge

* fix import issue

* run format

* update configs
2025-11-17 08:23:23 -06:00

185 lines
4.9 KiB
TypeScript

"use client";
import React, {
createContext,
useContext,
useState,
useEffect,
useCallback,
ReactNode,
} from "react";
interface User {
user_id: string;
email: string;
name: string;
picture?: string;
provider: string;
last_login?: string;
}
interface AuthContextType {
user: User | null;
isLoading: boolean;
isAuthenticated: boolean;
isNoAuthMode: boolean;
login: () => void;
logout: () => Promise<void>;
refreshAuth: () => Promise<void>;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function useAuth() {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error("useAuth must be used within an AuthProvider");
}
return context;
}
interface AuthProviderProps {
children: ReactNode;
}
export function AuthProvider({ children }: AuthProviderProps) {
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [isNoAuthMode, setIsNoAuthMode] = useState(false);
const checkAuth = useCallback(async () => {
try {
const response = await fetch("/api/auth/me");
// If we can't reach the backend, keep loading
if (!response.ok && (response.status === 0 || response.status >= 500)) {
console.log("Backend not ready, retrying in 2 seconds...");
setTimeout(checkAuth, 2000);
return;
}
const data = await response.json();
// Check if we're in no-auth mode
if (data.no_auth_mode) {
setIsNoAuthMode(true);
setUser(null);
} else if (data.authenticated && data.user) {
setIsNoAuthMode(false);
setUser(data.user);
} else {
setIsNoAuthMode(false);
setUser(null);
}
setIsLoading(false);
} catch (error) {
console.error("Auth check failed:", error);
// Network error - backend not ready, keep loading and retry
console.log("Backend not ready, retrying in 2 seconds...");
setTimeout(checkAuth, 2000);
}
}, []);
const login = () => {
// Don't allow login in no-auth mode
if (isNoAuthMode) {
console.log("Login attempted in no-auth mode - ignored");
return;
}
// Use the correct auth callback URL, not connectors callback
const redirectUri = `${window.location.origin}/auth/callback`;
console.log("Starting login with redirect URI:", redirectUri);
fetch("/api/auth/init", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
connector_type: "google_drive",
purpose: "app_auth",
name: "App Authentication",
redirect_uri: redirectUri,
}),
})
.then((response) => response.json())
.then((result) => {
console.log("Auth init response:", result);
if (result.oauth_config) {
// Store that this is for app authentication
localStorage.setItem("auth_purpose", "app_auth");
localStorage.setItem("connecting_connector_id", result.connection_id);
localStorage.setItem("connecting_connector_type", "app_auth");
console.log("Stored localStorage items:", {
auth_purpose: localStorage.getItem("auth_purpose"),
connecting_connector_id: localStorage.getItem(
"connecting_connector_id",
),
connecting_connector_type: localStorage.getItem(
"connecting_connector_type",
),
});
const authUrl =
`${result.oauth_config.authorization_endpoint}?` +
`client_id=${result.oauth_config.client_id}&` +
`response_type=code&` +
`scope=${result.oauth_config.scopes.join(" ")}&` +
`redirect_uri=${encodeURIComponent(result.oauth_config.redirect_uri)}&` +
`access_type=offline&` +
`prompt=consent&` +
`state=${result.connection_id}`;
console.log("Redirecting to OAuth URL:", authUrl);
window.location.href = authUrl;
} else {
console.error("No oauth_config in response:", result);
}
})
.catch((error) => {
console.error("Login failed:", error);
});
};
const logout = async () => {
// Don't allow logout in no-auth mode
if (isNoAuthMode) {
console.log("Logout attempted in no-auth mode - ignored");
return;
}
try {
await fetch("/api/auth/logout", {
method: "POST",
});
setUser(null);
} catch (error) {
console.error("Logout failed:", error);
}
};
const refreshAuth = async () => {
await checkAuth();
};
useEffect(() => {
checkAuth();
}, [checkAuth]);
const value: AuthContextType = {
user,
isLoading,
isAuthenticated: !!user,
isNoAuthMode,
login,
logout,
refreshAuth,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}