wu981526092 commited on
Commit
ba6eacd
Β·
1 Parent(s): 5a2b931

Fix HF Spaces iframe session persistence with localStorage fallback

Browse files

πŸ”§ Enhanced Authentication for HF Spaces iframe:
- Added localStorage backup for user data in login success page
- Frontend sends localStorage data as header fallback
- Backend checks X-HF-User-Backup header when session fails
- Improved message passing between login window and parent
- Session data restored from localStorage backup

πŸ’‘ This addresses the critical iframe cookie issues in HF Spaces
where session cookies may not persist properly across windows.

πŸ”„ Flow: Login β†’ localStorage backup β†’ Header fallback β†’ Session restore

backend/dependencies.py CHANGED
@@ -115,7 +115,7 @@ def get_current_user_optional(request: Request) -> Optional[Dict[str, Any]]:
115
  try:
116
  user = request.session.get("user")
117
  if user:
118
- logger.info(f"πŸ”“ Found authenticated user: {user.get('username', 'unknown')}")
119
  return user
120
  else:
121
  # Add detailed debugging for session contents
@@ -134,6 +134,20 @@ def get_current_user_optional(request: Request) -> Optional[Dict[str, Any]]:
134
  session_data = dict(request.session) if hasattr(request.session, 'keys') else {}
135
  logger.warning(f"πŸ” HF Session data: {session_data}")
136
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  # Check request headers that might affect session
138
  relevant_headers = {
139
  'host': request.headers.get('host'),
 
115
  try:
116
  user = request.session.get("user")
117
  if user:
118
+ logger.info(f"πŸ”“ Found authenticated user in session: {user.get('username', 'unknown')}")
119
  return user
120
  else:
121
  # Add detailed debugging for session contents
 
134
  session_data = dict(request.session) if hasattr(request.session, 'keys') else {}
135
  logger.warning(f"πŸ” HF Session data: {session_data}")
136
 
137
+ # FALLBACK: Check for localStorage backup data via special header
138
+ # Frontend can send this as a custom header when localStorage is available
139
+ localStorage_user = request.headers.get("X-HF-User-Backup")
140
+ if localStorage_user:
141
+ try:
142
+ import json
143
+ backup_user = json.loads(localStorage_user)
144
+ logger.info(f"πŸ”„ Using localStorage backup user: {backup_user.get('username', 'unknown')}")
145
+ # Store back in session for next requests
146
+ request.session["user"] = backup_user
147
+ return backup_user
148
+ except (json.JSONDecodeError, ValueError) as e:
149
+ logger.warning(f"Could not parse localStorage backup user: {e}")
150
+
151
  # Check request headers that might affect session
152
  relevant_headers = {
153
  'host': request.headers.get('host'),
backend/routers/auth.py CHANGED
@@ -7,6 +7,7 @@ These routes are only active when running in HF Spaces environment.
7
  import os
8
  import logging
9
  import secrets
 
10
  from typing import Optional
11
  from pathlib import Path
12
  from fastapi import APIRouter, Request, Response, HTTPException
@@ -255,13 +256,17 @@ async def oauth_callback(request: Request, code: str, state: str):
255
 
256
  # For HF Spaces, show a success page that notifies parent window and closes
257
  if is_huggingface_space():
258
- success_html = """
 
 
 
 
259
  <!DOCTYPE html>
260
  <html>
261
  <head>
262
  <title>Login Successful</title>
263
  <style>
264
- body {
265
  font-family: Arial, sans-serif;
266
  text-align: center;
267
  padding: 50px;
@@ -273,53 +278,65 @@ async def oauth_callback(request: Request, code: str, state: str):
273
  align-items: center;
274
  justify-content: center;
275
  flex-direction: column;
276
- }
277
- .success-card {
278
  background: rgba(255,255,255,0.1);
279
  padding: 40px;
280
  border-radius: 20px;
281
  backdrop-filter: blur(10px);
282
  border: 1px solid rgba(255,255,255,0.2);
283
- }
284
- .checkmark {
285
  font-size: 60px;
286
  margin-bottom: 20px;
287
  color: #4CAF50;
288
- }
289
  </style>
290
  </head>
291
  <body>
292
  <div class="success-card">
293
  <div class="checkmark">βœ…</div>
294
  <h1>Login Successful!</h1>
 
295
  <p>You have successfully logged in with your Hugging Face account.</p>
296
  <p>This tab will close automatically...</p>
297
  </div>
298
 
299
  <script>
300
- // Try to send message to parent window
301
- try {
302
- if (window.opener) {
303
- window.opener.postMessage({
 
 
 
 
 
 
 
 
 
 
304
  type: 'HF_LOGIN_SUCCESS',
 
305
  timestamp: Date.now()
306
- }, window.location.origin);
307
  console.log('βœ… Sent login success message to parent window');
308
- }
309
- } catch (e) {
310
  console.log('Could not send message to parent:', e);
311
- }
312
 
313
  // Close this tab after a short delay
314
- setTimeout(() => {
315
- try {
316
  window.close();
317
- } catch (e) {
318
  console.log('Could not close window:', e);
319
  // Redirect to main app if we can't close
320
  window.location.href = '/';
321
- }
322
- }, 2000);
323
  </script>
324
  </body>
325
  </html>
 
7
  import os
8
  import logging
9
  import secrets
10
+ import json
11
  from typing import Optional
12
  from pathlib import Path
13
  from fastapi import APIRouter, Request, Response, HTTPException
 
256
 
257
  # For HF Spaces, show a success page that notifies parent window and closes
258
  if is_huggingface_space():
259
+ # Get user data to pass to parent window
260
+ user_data = request.session.get("user", {})
261
+ user_json = json.dumps(user_data)
262
+
263
+ success_html = f"""
264
  <!DOCTYPE html>
265
  <html>
266
  <head>
267
  <title>Login Successful</title>
268
  <style>
269
+ body {{
270
  font-family: Arial, sans-serif;
271
  text-align: center;
272
  padding: 50px;
 
278
  align-items: center;
279
  justify-content: center;
280
  flex-direction: column;
281
+ }}
282
+ .success-card {{
283
  background: rgba(255,255,255,0.1);
284
  padding: 40px;
285
  border-radius: 20px;
286
  backdrop-filter: blur(10px);
287
  border: 1px solid rgba(255,255,255,0.2);
288
+ }}
289
+ .checkmark {{
290
  font-size: 60px;
291
  margin-bottom: 20px;
292
  color: #4CAF50;
293
+ }}
294
  </style>
295
  </head>
296
  <body>
297
  <div class="success-card">
298
  <div class="checkmark">βœ…</div>
299
  <h1>Login Successful!</h1>
300
+ <p>Welcome, {user_data.get('name', 'User')}!</p>
301
  <p>You have successfully logged in with your Hugging Face account.</p>
302
  <p>This tab will close automatically...</p>
303
  </div>
304
 
305
  <script>
306
+ // Store user data in localStorage as backup (for iframe issues)
307
+ const userData = {user_json};
308
+ try {{
309
+ localStorage.setItem('hf_user', JSON.stringify(userData));
310
+ localStorage.setItem('hf_login_timestamp', Date.now().toString());
311
+ console.log('βœ… Stored user data in localStorage as backup');
312
+ }} catch (e) {{
313
+ console.log('Could not store in localStorage:', e);
314
+ }}
315
+
316
+ // Try to send message to parent window with user data
317
+ try {{
318
+ if (window.opener) {{
319
+ window.opener.postMessage({{
320
  type: 'HF_LOGIN_SUCCESS',
321
+ userData: userData,
322
  timestamp: Date.now()
323
+ }}, window.location.origin);
324
  console.log('βœ… Sent login success message to parent window');
325
+ }}
326
+ }} catch (e) {{
327
  console.log('Could not send message to parent:', e);
328
+ }}
329
 
330
  // Close this tab after a short delay
331
+ setTimeout(() => {{
332
+ try {{
333
  window.close();
334
+ }} catch (e) {{
335
  console.log('Could not close window:', e);
336
  // Redirect to main app if we can't close
337
  window.location.href = '/';
338
+ }}
339
+ }}, 2000);
340
  </script>
341
  </body>
342
  </html>
frontend/src/context/AuthContext.tsx CHANGED
@@ -59,7 +59,20 @@ export function AuthProvider({ children }: AuthProviderProps) {
59
  setIsLoading(true);
60
  console.log("πŸ” Checking authentication status...");
61
 
62
- const response = await fetch("/auth/status");
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  const data = await response.json();
64
 
65
  console.log("πŸ“Š Auth status response:", data);
@@ -160,9 +173,21 @@ export function AuthProvider({ children }: AuthProviderProps) {
160
  if (event.origin !== window.location.origin) {
161
  return; // Only accept messages from same origin
162
  }
163
-
164
  if (event.data.type === "HF_LOGIN_SUCCESS") {
165
  console.log("βœ… Login success message received from popup");
 
 
 
 
 
 
 
 
 
 
 
 
166
  setShowLoginModal(false);
167
  // Refresh auth status after successful login
168
  setTimeout(() => checkAuthStatus(), 1000);
 
59
  setIsLoading(true);
60
  console.log("πŸ” Checking authentication status...");
61
 
62
+ // Prepare headers with localStorage backup if available
63
+ const headers: Record<string, string> = {};
64
+ try {
65
+ const localUser = localStorage.getItem('hf_user');
66
+ if (localUser) {
67
+ // Send localStorage backup as header for HF Spaces iframe issues
68
+ headers['X-HF-User-Backup'] = localUser;
69
+ console.log("πŸ“¦ Sending localStorage backup with auth check");
70
+ }
71
+ } catch (e) {
72
+ console.warn("Could not access localStorage:", e);
73
+ }
74
+
75
+ const response = await fetch("/auth/status", { headers });
76
  const data = await response.json();
77
 
78
  console.log("πŸ“Š Auth status response:", data);
 
173
  if (event.origin !== window.location.origin) {
174
  return; // Only accept messages from same origin
175
  }
176
+
177
  if (event.data.type === "HF_LOGIN_SUCCESS") {
178
  console.log("βœ… Login success message received from popup");
179
+
180
+ // If user data is provided, store it locally as backup
181
+ if (event.data.userData) {
182
+ try {
183
+ localStorage.setItem('hf_user', JSON.stringify(event.data.userData));
184
+ localStorage.setItem('hf_login_timestamp', Date.now().toString());
185
+ console.log("πŸ“¦ Stored user data in localStorage as backup");
186
+ } catch (e) {
187
+ console.warn("Could not store user data in localStorage:", e);
188
+ }
189
+ }
190
+
191
  setShowLoginModal(false);
192
  // Refresh auth status after successful login
193
  setTimeout(() => checkAuthStatus(), 1000);