Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
Deploy: Consolidated gold tables, fixed nginx docs routing
Browse files- api/routes/auth.py +70 -29
- api/static/index.html +1 -1
- frontend/src/pages/HomeModern.tsx +1 -1
api/routes/auth.py
CHANGED
|
@@ -272,6 +272,12 @@ async def oauth_callback(
|
|
| 272 |
|
| 273 |
# Exchange code for access token
|
| 274 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 275 |
async with httpx.AsyncClient() as client:
|
| 276 |
token_response = await client.post(
|
| 277 |
config['token_url'],
|
|
@@ -285,7 +291,12 @@ async def oauth_callback(
|
|
| 285 |
headers={'Accept': 'application/json'}
|
| 286 |
)
|
| 287 |
|
|
|
|
|
|
|
| 288 |
if token_response.status_code != 200:
|
|
|
|
|
|
|
|
|
|
| 289 |
# Parse error response for user-friendly message
|
| 290 |
error_msg = "Authentication failed"
|
| 291 |
try:
|
|
@@ -317,20 +328,27 @@ async def oauth_callback(
|
|
| 317 |
return RedirectResponse(url=f"{redirect_url}?{params}")
|
| 318 |
|
| 319 |
token_data = token_response.json()
|
|
|
|
| 320 |
access_token = token_data.get('access_token')
|
| 321 |
|
| 322 |
if not access_token:
|
|
|
|
| 323 |
logger.error(f"No access token in response from {provider}: {token_data}")
|
| 324 |
frontend_url = os.getenv('FRONTEND_URL', '')
|
| 325 |
redirect_url = oauth_state.redirect_uri or (frontend_url if frontend_url and 'localhost' not in frontend_url else '/')
|
| 326 |
params = urlencode({'error': f'{provider.title()} login failed: No access token received'})
|
| 327 |
return RedirectResponse(url=f"{redirect_url}?{params}")
|
| 328 |
|
|
|
|
|
|
|
| 329 |
# Get user info from provider
|
|
|
|
| 330 |
user_info = await get_user_info(provider, access_token, config)
|
|
|
|
| 331 |
|
| 332 |
# Validate we got user info (email is now always set, even if placeholder for Facebook)
|
| 333 |
if not user_info:
|
|
|
|
| 334 |
logger.error(f"Could not retrieve user info from {provider}. Check API response logs above.")
|
| 335 |
frontend_url = os.getenv('FRONTEND_URL', '')
|
| 336 |
redirect_url = oauth_state.redirect_uri or (frontend_url if frontend_url and 'localhost' not in frontend_url else '/')
|
|
@@ -415,38 +433,61 @@ async def get_user_info(provider: str, access_token: str, config: dict) -> dict:
|
|
| 415 |
elif provider == 'facebook':
|
| 416 |
# Facebook uses access token as query parameter, not Bearer header
|
| 417 |
userinfo_url_with_token = f"{config['userinfo_url']}&access_token={access_token}"
|
| 418 |
-
resp = await client.get(userinfo_url_with_token)
|
| 419 |
|
| 420 |
-
|
| 421 |
-
logger.info(f"
|
| 422 |
-
logger.info(f"Facebook API response: {resp.text[:500]}")
|
| 423 |
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 433 |
return None
|
| 434 |
-
|
| 435 |
-
# Facebook may not return email if permission not approved in App Review
|
| 436 |
-
# Generate a placeholder email using Facebook ID if email not available
|
| 437 |
-
email = data.get('email')
|
| 438 |
-
if not email:
|
| 439 |
-
fb_id = data.get('id')
|
| 440 |
-
email = f"facebook_{fb_id}@communityone.placeholder"
|
| 441 |
-
logger.warning(f"Facebook did not return email for user {fb_id}. Using placeholder: {email}")
|
| 442 |
-
|
| 443 |
-
user_info = {
|
| 444 |
-
'email': email,
|
| 445 |
-
'oauth_id': str(data.get('id')),
|
| 446 |
-
'full_name': data.get('name'),
|
| 447 |
-
'avatar_url': data.get('picture', {}).get('data', {}).get('url') if isinstance(data.get('picture'), dict) else None,
|
| 448 |
-
'username': data.get('name', '').replace(' ', '_').lower(),
|
| 449 |
-
}
|
| 450 |
|
| 451 |
elif provider == 'github':
|
| 452 |
# Get user profile
|
|
|
|
| 272 |
|
| 273 |
# Exchange code for access token
|
| 274 |
try:
|
| 275 |
+
logger.info(f"π [{provider.upper()}] Starting token exchange")
|
| 276 |
+
logger.info(f"π [{provider.upper()}] Token URL: {config['token_url']}")
|
| 277 |
+
logger.info(f"π [{provider.upper()}] Callback URL: {callback_url}")
|
| 278 |
+
logger.info(f"π [{provider.upper()}] Client ID configured: {bool(client_id)}")
|
| 279 |
+
logger.info(f"π [{provider.upper()}] Client Secret configured: {bool(client_secret)}")
|
| 280 |
+
|
| 281 |
async with httpx.AsyncClient() as client:
|
| 282 |
token_response = await client.post(
|
| 283 |
config['token_url'],
|
|
|
|
| 291 |
headers={'Accept': 'application/json'}
|
| 292 |
)
|
| 293 |
|
| 294 |
+
logger.info(f"π [{provider.upper()}] Token response status: {token_response.status_code}")
|
| 295 |
+
|
| 296 |
if token_response.status_code != 200:
|
| 297 |
+
logger.error(f"β [{provider.upper()}] Token exchange failed!")
|
| 298 |
+
logger.error(f"β [{provider.upper()}] Response: {token_response.text}")
|
| 299 |
+
|
| 300 |
# Parse error response for user-friendly message
|
| 301 |
error_msg = "Authentication failed"
|
| 302 |
try:
|
|
|
|
| 328 |
return RedirectResponse(url=f"{redirect_url}?{params}")
|
| 329 |
|
| 330 |
token_data = token_response.json()
|
| 331 |
+
logger.info(f"β
[{provider.upper()}] Token response keys: {list(token_data.keys())}")
|
| 332 |
access_token = token_data.get('access_token')
|
| 333 |
|
| 334 |
if not access_token:
|
| 335 |
+
logger.error(f"β [{provider.upper()}] No access token in response!")
|
| 336 |
logger.error(f"No access token in response from {provider}: {token_data}")
|
| 337 |
frontend_url = os.getenv('FRONTEND_URL', '')
|
| 338 |
redirect_url = oauth_state.redirect_uri or (frontend_url if frontend_url and 'localhost' not in frontend_url else '/')
|
| 339 |
params = urlencode({'error': f'{provider.title()} login failed: No access token received'})
|
| 340 |
return RedirectResponse(url=f"{redirect_url}?{params}")
|
| 341 |
|
| 342 |
+
logger.info(f"β
[{provider.upper()}] Got access token (first 20 chars): {access_token[:20]}...")
|
| 343 |
+
|
| 344 |
# Get user info from provider
|
| 345 |
+
logger.info(f"π [{provider.upper()}] Fetching user info...")
|
| 346 |
user_info = await get_user_info(provider, access_token, config)
|
| 347 |
+
logger.info(f"π [{provider.upper()}] User info result: {user_info is not None}")
|
| 348 |
|
| 349 |
# Validate we got user info (email is now always set, even if placeholder for Facebook)
|
| 350 |
if not user_info:
|
| 351 |
+
logger.error(f"β [{provider.upper()}] Could not retrieve user info! Check API response logs above.")
|
| 352 |
logger.error(f"Could not retrieve user info from {provider}. Check API response logs above.")
|
| 353 |
frontend_url = os.getenv('FRONTEND_URL', '')
|
| 354 |
redirect_url = oauth_state.redirect_uri or (frontend_url if frontend_url and 'localhost' not in frontend_url else '/')
|
|
|
|
| 433 |
elif provider == 'facebook':
|
| 434 |
# Facebook uses access token as query parameter, not Bearer header
|
| 435 |
userinfo_url_with_token = f"{config['userinfo_url']}&access_token={access_token}"
|
|
|
|
| 436 |
|
| 437 |
+
logger.info(f"π [FACEBOOK] Requesting user info from: {config['userinfo_url']}")
|
| 438 |
+
logger.info(f"π [FACEBOOK] Access token (first 20 chars): {access_token[:20]}...")
|
|
|
|
| 439 |
|
| 440 |
+
try:
|
| 441 |
+
resp = await client.get(userinfo_url_with_token)
|
| 442 |
+
|
| 443 |
+
# Enhanced logging for debugging
|
| 444 |
+
logger.info(f"π [FACEBOOK] API response status: {resp.status_code}")
|
| 445 |
+
logger.info(f"π [FACEBOOK] API response headers: {dict(resp.headers)}")
|
| 446 |
+
logger.info(f"π [FACEBOOK] API response body: {resp.text[:1000]}")
|
| 447 |
+
|
| 448 |
+
if resp.status_code != 200:
|
| 449 |
+
logger.error(f"β [FACEBOOK] Userinfo request failed!")
|
| 450 |
+
logger.error(f"β [FACEBOOK] Status: {resp.status_code}")
|
| 451 |
+
logger.error(f"β [FACEBOOK] Full response: {resp.text}")
|
| 452 |
+
return None
|
| 453 |
+
|
| 454 |
+
data = resp.json()
|
| 455 |
+
logger.info(f"β
[FACEBOOK] Successfully parsed JSON response")
|
| 456 |
+
logger.info(f"β
[FACEBOOK] Data keys: {list(data.keys())}")
|
| 457 |
+
|
| 458 |
+
# Validate we got required data
|
| 459 |
+
if not data.get('id'):
|
| 460 |
+
logger.error(f"β [FACEBOOK] Missing user ID in response!")
|
| 461 |
+
logger.error(f"β [FACEBOOK] Full response data: {data}")
|
| 462 |
+
return None
|
| 463 |
+
|
| 464 |
+
logger.info(f"β
[FACEBOOK] Got user ID: {data.get('id')}")
|
| 465 |
+
|
| 466 |
+
# Facebook may not return email if permission not approved in App Review
|
| 467 |
+
# Generate a placeholder email using Facebook ID if email not available
|
| 468 |
+
email = data.get('email')
|
| 469 |
+
if not email:
|
| 470 |
+
fb_id = data.get('id')
|
| 471 |
+
email = f"facebook_{fb_id}@communityone.placeholder"
|
| 472 |
+
logger.warning(f"β οΈ [FACEBOOK] No email returned for user {fb_id}. Using placeholder: {email}")
|
| 473 |
+
else:
|
| 474 |
+
logger.info(f"β
[FACEBOOK] Got email: {email}")
|
| 475 |
+
|
| 476 |
+
user_info = {
|
| 477 |
+
'email': email,
|
| 478 |
+
'oauth_id': str(data.get('id')),
|
| 479 |
+
'full_name': data.get('name'),
|
| 480 |
+
'avatar_url': data.get('picture', {}).get('data', {}).get('url') if isinstance(data.get('picture'), dict) else None,
|
| 481 |
+
'username': data.get('name', '').replace(' ', '_').lower(),
|
| 482 |
+
}
|
| 483 |
+
logger.info(f"β
[FACEBOOK] Created user_info: {user_info}")
|
| 484 |
+
|
| 485 |
+
except Exception as e:
|
| 486 |
+
logger.error(f"β [FACEBOOK] Exception during user info fetch: {str(e)}")
|
| 487 |
+
logger.error(f"β [FACEBOOK] Exception type: {type(e).__name__}")
|
| 488 |
+
import traceback
|
| 489 |
+
logger.error(f"β [FACEBOOK] Traceback: {traceback.format_exc()}")
|
| 490 |
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 491 |
|
| 492 |
elif provider == 'github':
|
| 493 |
# Get user profile
|
api/static/index.html
CHANGED
|
@@ -85,7 +85,7 @@
|
|
| 85 |
}
|
| 86 |
}
|
| 87 |
</script>
|
| 88 |
-
<script type="module" crossorigin src="/assets/index-
|
| 89 |
<link rel="stylesheet" crossorigin href="/assets/index-DHPGfrfk.css">
|
| 90 |
</head>
|
| 91 |
<body>
|
|
|
|
| 85 |
}
|
| 86 |
}
|
| 87 |
</script>
|
| 88 |
+
<script type="module" crossorigin src="/assets/index-BiXTU5yP.js"></script>
|
| 89 |
<link rel="stylesheet" crossorigin href="/assets/index-DHPGfrfk.css">
|
| 90 |
</head>
|
| 91 |
<body>
|
frontend/src/pages/HomeModern.tsx
CHANGED
|
@@ -1465,7 +1465,7 @@ export default function HomeModern() {
|
|
| 1465 |
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/>
|
| 1466 |
</svg>
|
| 1467 |
</a>
|
| 1468 |
-
<a href="https://github.com/getcommunityone
|
| 1469 |
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
| 1470 |
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
| 1471 |
</svg>
|
|
|
|
| 1465 |
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/>
|
| 1466 |
</svg>
|
| 1467 |
</a>
|
| 1468 |
+
<a href="https://github.com/getcommunityone" target="_blank" rel="noopener noreferrer" className="text-gray-400 hover:text-white transition-colors" aria-label="GitHub">
|
| 1469 |
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
| 1470 |
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
| 1471 |
</svg>
|