Spaces:
Running
Running
Commit
·
0d7ac2b
1
Parent(s):
aad2775
Fix login modal authentication issues
Browse files🔧 Critical Fixes:
- Fix YouTube iframe permissions policy violations (removed web-share)
- Fix authentication loop after successful OAuth login
- Improve auth status endpoint to return complete user data
- Add URL parameter detection for successful authentication
- Add page focus listener to refresh auth status
- Enhanced logging for better debugging
🐛 Issues Resolved:
- Users no longer get stuck in login loop after OAuth success
- Iframe permission violations eliminated
- Auth state properly recognized after login
- Login modal correctly disappears after successful authentication
These fixes address the core authentication flow issues
identified in HF Spaces deployment.
backend/routers/auth.py
CHANGED
|
@@ -42,8 +42,12 @@ async def auth_status(request: Request):
|
|
| 42 |
"login_required": True, # Mandatory for OpenAI API protection
|
| 43 |
"user_authenticated": bool(user),
|
| 44 |
"user_info": {
|
| 45 |
-
"
|
| 46 |
"username": user.get("username") if user else None,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
} if user else None,
|
| 48 |
"hf_sign_detected": bool(request.query_params.get("__sign")) if is_huggingface_space() else False,
|
| 49 |
}
|
|
@@ -245,8 +249,8 @@ async def oauth_callback(request: Request, code: str, state: str):
|
|
| 245 |
|
| 246 |
logger.info(f"User logged in: {user_info.get('name')} ({user_info.get('login')})")
|
| 247 |
|
| 248 |
-
# Redirect to main application
|
| 249 |
-
return RedirectResponse(url="
|
| 250 |
|
| 251 |
|
| 252 |
@router.get("/logout")
|
|
|
|
| 42 |
"login_required": True, # Mandatory for OpenAI API protection
|
| 43 |
"user_authenticated": bool(user),
|
| 44 |
"user_info": {
|
| 45 |
+
"id": user.get("id") if user else None,
|
| 46 |
"username": user.get("username") if user else None,
|
| 47 |
+
"name": user.get("name") if user else None,
|
| 48 |
+
"email": user.get("email") if user else None,
|
| 49 |
+
"avatar_url": user.get("avatar_url") if user else None,
|
| 50 |
+
"auth_method": user.get("auth_method") if user else None,
|
| 51 |
} if user else None,
|
| 52 |
"hf_sign_detected": bool(request.query_params.get("__sign")) if is_huggingface_space() else False,
|
| 53 |
}
|
|
|
|
| 249 |
|
| 250 |
logger.info(f"User logged in: {user_info.get('name')} ({user_info.get('login')})")
|
| 251 |
|
| 252 |
+
# Redirect to main application with auth success indicator
|
| 253 |
+
return RedirectResponse(url="/?auth=success", status_code=302)
|
| 254 |
|
| 255 |
|
| 256 |
@router.get("/logout")
|
frontend/src/components/auth/LoginModal.tsx
CHANGED
|
@@ -95,7 +95,7 @@ export function LoginModal({ isOpen, onClose }: LoginModalProps) {
|
|
| 95 |
<iframe
|
| 96 |
src="https://www.youtube.com/embed/btrS9pfDYJY?si=dDX4tIs-oS2O2d2p"
|
| 97 |
title="AgentGraph: Interactive Analysis Platform Demo"
|
| 98 |
-
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture
|
| 99 |
allowFullScreen
|
| 100 |
className="w-full h-full rounded-xl"
|
| 101 |
/>
|
|
|
|
| 95 |
<iframe
|
| 96 |
src="https://www.youtube.com/embed/btrS9pfDYJY?si=dDX4tIs-oS2O2d2p"
|
| 97 |
title="AgentGraph: Interactive Analysis Platform Demo"
|
| 98 |
+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
| 99 |
allowFullScreen
|
| 100 |
className="w-full h-full rounded-xl"
|
| 101 |
/>
|
frontend/src/context/AuthContext.tsx
CHANGED
|
@@ -67,7 +67,7 @@ export function AuthProvider({ children }: AuthProviderProps) {
|
|
| 67 |
|
| 68 |
if (data.user_authenticated && data.user_info) {
|
| 69 |
setUser({
|
| 70 |
-
id: data.user_info.
|
| 71 |
username: data.user_info.username || "Unknown User",
|
| 72 |
name:
|
| 73 |
data.user_info.name || data.user_info.username || "Unknown User",
|
|
@@ -76,8 +76,13 @@ export function AuthProvider({ children }: AuthProviderProps) {
|
|
| 76 |
auth_method: data.user_info.auth_method || "unknown",
|
| 77 |
});
|
| 78 |
setShowLoginModal(false);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
} else {
|
| 80 |
setUser(null);
|
|
|
|
| 81 |
// Show login modal if in HF Spaces and auth is required
|
| 82 |
if (data.environment === "huggingface_spaces" && data.login_required) {
|
| 83 |
setShowLoginModal(true);
|
|
@@ -125,7 +130,17 @@ export function AuthProvider({ children }: AuthProviderProps) {
|
|
| 125 |
};
|
| 126 |
|
| 127 |
useEffect(() => {
|
| 128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
// Listen for auth-required events from API calls
|
| 131 |
const handleAuthRequired = () => {
|
|
@@ -134,12 +149,20 @@ export function AuthProvider({ children }: AuthProviderProps) {
|
|
| 134 |
}
|
| 135 |
};
|
| 136 |
|
| 137 |
-
|
| 138 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
return () => {
|
| 140 |
-
window.removeEventListener(
|
|
|
|
| 141 |
};
|
| 142 |
-
}, [
|
| 143 |
|
| 144 |
const value: AuthContextType = {
|
| 145 |
user,
|
|
|
|
| 67 |
|
| 68 |
if (data.user_authenticated && data.user_info) {
|
| 69 |
setUser({
|
| 70 |
+
id: data.user_info.id || "unknown",
|
| 71 |
username: data.user_info.username || "Unknown User",
|
| 72 |
name:
|
| 73 |
data.user_info.name || data.user_info.username || "Unknown User",
|
|
|
|
| 76 |
auth_method: data.user_info.auth_method || "unknown",
|
| 77 |
});
|
| 78 |
setShowLoginModal(false);
|
| 79 |
+
console.log(
|
| 80 |
+
"✅ User authenticated successfully:",
|
| 81 |
+
data.user_info.username
|
| 82 |
+
);
|
| 83 |
} else {
|
| 84 |
setUser(null);
|
| 85 |
+
console.log("❌ User not authenticated - showing login modal");
|
| 86 |
// Show login modal if in HF Spaces and auth is required
|
| 87 |
if (data.environment === "huggingface_spaces" && data.login_required) {
|
| 88 |
setShowLoginModal(true);
|
|
|
|
| 130 |
};
|
| 131 |
|
| 132 |
useEffect(() => {
|
| 133 |
+
// Check for auth success parameter in URL
|
| 134 |
+
const urlParams = new URLSearchParams(window.location.search);
|
| 135 |
+
if (urlParams.get('auth') === 'success') {
|
| 136 |
+
console.log("Authentication success detected - refreshing status");
|
| 137 |
+
// Remove the parameter from URL
|
| 138 |
+
window.history.replaceState({}, document.title, window.location.pathname);
|
| 139 |
+
// Force auth status check
|
| 140 |
+
setTimeout(() => checkAuthStatus(), 100);
|
| 141 |
+
} else {
|
| 142 |
+
checkAuthStatus();
|
| 143 |
+
}
|
| 144 |
|
| 145 |
// Listen for auth-required events from API calls
|
| 146 |
const handleAuthRequired = () => {
|
|
|
|
| 149 |
}
|
| 150 |
};
|
| 151 |
|
| 152 |
+
// Listen for page focus to refresh auth status (useful after OAuth redirect)
|
| 153 |
+
const handleFocus = () => {
|
| 154 |
+
console.log("Page focused - checking auth status");
|
| 155 |
+
checkAuthStatus();
|
| 156 |
+
};
|
| 157 |
+
|
| 158 |
+
window.addEventListener("auth-required", handleAuthRequired);
|
| 159 |
+
window.addEventListener("focus", handleFocus);
|
| 160 |
+
|
| 161 |
return () => {
|
| 162 |
+
window.removeEventListener("auth-required", handleAuthRequired);
|
| 163 |
+
window.removeEventListener("focus", handleFocus);
|
| 164 |
};
|
| 165 |
+
}, []);
|
| 166 |
|
| 167 |
const value: AuthContextType = {
|
| 168 |
user,
|
frontend/src/lib/api.ts
CHANGED
|
@@ -36,13 +36,17 @@ async function fetchApi<T>(
|
|
| 36 |
// Handle 401 (Unauthorized) - trigger login modal
|
| 37 |
if (response.status === 401) {
|
| 38 |
// Check if running in browser environment
|
| 39 |
-
if (typeof window !==
|
| 40 |
// Dispatch a custom event to trigger login modal
|
| 41 |
-
window.dispatchEvent(
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
}
|
| 45 |
-
throw new ApiError(response.status,
|
| 46 |
}
|
| 47 |
|
| 48 |
// Handle 429 (Too Many Requests) with exponential backoff
|
|
|
|
| 36 |
// Handle 401 (Unauthorized) - trigger login modal
|
| 37 |
if (response.status === 401) {
|
| 38 |
// Check if running in browser environment
|
| 39 |
+
if (typeof window !== "undefined") {
|
| 40 |
// Dispatch a custom event to trigger login modal
|
| 41 |
+
window.dispatchEvent(
|
| 42 |
+
new CustomEvent("auth-required", {
|
| 43 |
+
detail: {
|
| 44 |
+
message: "Authentication required to access this feature",
|
| 45 |
+
},
|
| 46 |
+
})
|
| 47 |
+
);
|
| 48 |
}
|
| 49 |
+
throw new ApiError(response.status, "Authentication required");
|
| 50 |
}
|
| 51 |
|
| 52 |
// Handle 429 (Too Many Requests) with exponential backoff
|