Mark-Lasfar commited on
Commit
7f8c5ab
·
1 Parent(s): 09451d2

Update backend and server frontend for OAuth JSON response, client-side navigation, and add .gitignore

Browse files
Files changed (2) hide show
  1. api/auth.py +45 -8
  2. templates/login.html +34 -25
api/auth.py CHANGED
@@ -15,6 +15,7 @@ from typing import Optional
15
  import os
16
  import logging
17
  import secrets
 
18
 
19
  from api.database import User, OAuthAccount, CustomSQLAlchemyUserDatabase, get_user_db
20
  from api.models import UserRead, UserCreate, UserUpdate
@@ -154,7 +155,6 @@ current_active_user = fastapi_users.current_user(active=True, optional=True)
154
 
155
  # --- إضافة custom authorize endpoints (يرجع JSON مع authorization_url) ---
156
 
157
- # Custom Google Authorize
158
  async def custom_google_authorize(
159
  state: Optional[str] = None,
160
  oauth_client=Depends(lambda: google_oauth_client),
@@ -169,7 +169,6 @@ async def custom_google_authorize(
169
  "authorization_url": authorization_url
170
  }, status_code=200)
171
 
172
- # Custom GitHub Authorize
173
  async def custom_github_authorize(
174
  state: Optional[str] = None,
175
  oauth_client=Depends(lambda: github_oauth_client),
@@ -184,7 +183,7 @@ async def custom_github_authorize(
184
  "authorization_url": authorization_url
185
  }, status_code=200)
186
 
187
- # --- Custom Callback endpoints (بدون تغيير، يرجع JSON مع token) ---
188
 
189
  async def custom_oauth_callback(
190
  code: str,
@@ -195,9 +194,19 @@ async def custom_oauth_callback(
195
  ):
196
  logger.debug(f"Processing Google callback with code: {code}")
197
  try:
 
198
  token_data = await oauth_client.get_access_token(code, redirect_url)
199
  access_token = token_data["access_token"]
200
- user_info = await oauth_client.get_user_info(access_token)
 
 
 
 
 
 
 
 
 
201
 
202
  user = await user_manager.oauth_callback(
203
  oauth_name="google",
@@ -232,15 +241,43 @@ async def custom_github_oauth_callback(
232
  ):
233
  logger.debug(f"Processing GitHub callback with code: {code}")
234
  try:
 
235
  token_data = await oauth_client.get_access_token(code, redirect_url)
236
  access_token = token_data["access_token"]
237
- user_info = await oauth_client.get_user_info(access_token)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
  user = await user_manager.oauth_callback(
240
  oauth_name="github",
241
  access_token=access_token,
242
  account_id=str(user_info["id"]),
243
- account_email=user_info.get("email") or f"{user_info['login']}@github.com",
244
  expires_at=token_data.get("expires_in"),
245
  refresh_token=token_data.get("refresh_token"),
246
  associate_by_email=True,
@@ -260,7 +297,7 @@ async def custom_github_oauth_callback(
260
  logger.error(f"Error in GitHub OAuth callback: {str(e)}")
261
  return JSONResponse(content={"detail": str(e)}, status_code=400)
262
 
263
- # تضمين الراوترات داخل التطبيق (بدون OAuth routers هنا، هنضيفهم يدويًا)
264
  def get_auth_router(app: FastAPI):
265
  app.include_router(fastapi_users.get_auth_router(auth_backend), prefix="/auth/jwt", tags=["auth"])
266
  app.include_router(fastapi_users.get_register_router(UserRead, UserCreate), prefix="/auth", tags=["auth"])
@@ -268,7 +305,7 @@ def get_auth_router(app: FastAPI):
268
  app.include_router(fastapi_users.get_verify_router(UserRead), prefix="/auth", tags=["auth"])
269
  app.include_router(fastapi_users.get_users_router(UserRead, UserUpdate), prefix="/users", tags=["users"])
270
 
271
- # إضافة الـ custom OAuth endpoints يدويًا (authorize + callback)
272
  app.get("/auth/google/authorize")(custom_google_authorize)
273
  app.get("/auth/google/callback")(custom_oauth_callback)
274
  app.get("/auth/github/authorize")(custom_github_authorize)
 
15
  import os
16
  import logging
17
  import secrets
18
+ import httpx
19
 
20
  from api.database import User, OAuthAccount, CustomSQLAlchemyUserDatabase, get_user_db
21
  from api.models import UserRead, UserCreate, UserUpdate
 
155
 
156
  # --- إضافة custom authorize endpoints (يرجع JSON مع authorization_url) ---
157
 
 
158
  async def custom_google_authorize(
159
  state: Optional[str] = None,
160
  oauth_client=Depends(lambda: google_oauth_client),
 
169
  "authorization_url": authorization_url
170
  }, status_code=200)
171
 
 
172
  async def custom_github_authorize(
173
  state: Optional[str] = None,
174
  oauth_client=Depends(lambda: github_oauth_client),
 
183
  "authorization_url": authorization_url
184
  }, status_code=200)
185
 
186
+ # --- Custom Callback endpoints ---
187
 
188
  async def custom_oauth_callback(
189
  code: str,
 
194
  ):
195
  logger.debug(f"Processing Google callback with code: {code}")
196
  try:
197
+ # Get access token
198
  token_data = await oauth_client.get_access_token(code, redirect_url)
199
  access_token = token_data["access_token"]
200
+
201
+ # Manually fetch user info from Google API
202
+ async with httpx.AsyncClient() as client:
203
+ user_info_response = await client.get(
204
+ "https://www.googleapis.com/oauth2/v3/userinfo",
205
+ headers={"Authorization": f"Bearer {access_token}"}
206
+ )
207
+ if user_info_response.status_code != 200:
208
+ raise ValueError(f"Failed to fetch user info: {user_info_response.text}")
209
+ user_info = user_info_response.json()
210
 
211
  user = await user_manager.oauth_callback(
212
  oauth_name="google",
 
241
  ):
242
  logger.debug(f"Processing GitHub callback with code: {code}")
243
  try:
244
+ # Get access token
245
  token_data = await oauth_client.get_access_token(code, redirect_url)
246
  access_token = token_data["access_token"]
247
+
248
+ # Manually fetch user info from GitHub API
249
+ async with httpx.AsyncClient() as client:
250
+ user_info_response = await client.get(
251
+ "https://api.github.com/user",
252
+ headers={
253
+ "Authorization": f"Bearer {access_token}",
254
+ "Accept": "application/vnd.github.v3+json"
255
+ }
256
+ )
257
+ if user_info_response.status_code != 200:
258
+ raise ValueError(f"Failed to fetch user info: {user_info_response.text}")
259
+ user_info = user_info_response.json()
260
+
261
+ # Get email if not in user info
262
+ email = user_info.get("email")
263
+ if not email:
264
+ email_response = await client.get(
265
+ "https://api.github.com/user/emails",
266
+ headers={
267
+ "Authorization": f"Bearer {access_token}",
268
+ "Accept": "application/vnd.github.v3+json"
269
+ }
270
+ )
271
+ if email_response.status_code == 200:
272
+ emails = email_response.json()
273
+ primary_email = next((e["email"] for e in emails if e["primary"] and e["verified"]), None)
274
+ email = primary_email or f"{user_info['login']}@github.com"
275
 
276
  user = await user_manager.oauth_callback(
277
  oauth_name="github",
278
  access_token=access_token,
279
  account_id=str(user_info["id"]),
280
+ account_email=email,
281
  expires_at=token_data.get("expires_in"),
282
  refresh_token=token_data.get("refresh_token"),
283
  associate_by_email=True,
 
297
  logger.error(f"Error in GitHub OAuth callback: {str(e)}")
298
  return JSONResponse(content={"detail": str(e)}, status_code=400)
299
 
300
+ # تضمين الراوترات داخل التطبيق
301
  def get_auth_router(app: FastAPI):
302
  app.include_router(fastapi_users.get_auth_router(auth_backend), prefix="/auth/jwt", tags=["auth"])
303
  app.include_router(fastapi_users.get_register_router(UserRead, UserCreate), prefix="/auth", tags=["auth"])
 
305
  app.include_router(fastapi_users.get_verify_router(UserRead), prefix="/auth", tags=["auth"])
306
  app.include_router(fastapi_users.get_users_router(UserRead, UserUpdate), prefix="/users", tags=["users"])
307
 
308
+ # إضافة الـ custom OAuth endpoints يدويًا
309
  app.get("/auth/google/authorize")(custom_google_authorize)
310
  app.get("/auth/google/callback")(custom_oauth_callback)
311
  app.get("/auth/github/authorize")(custom_github_authorize)
templates/login.html CHANGED
@@ -433,7 +433,7 @@
433
  const token = localStorage.getItem('token');
434
  if (!token) {
435
  console.log('No token found in localStorage');
436
- return;
437
  }
438
  const response = await fetch('/api/verify-token', {
439
  method: 'GET',
@@ -445,13 +445,16 @@
445
  if (response.ok) {
446
  console.log('User is authenticated, redirecting to /chat');
447
  window.location.href = '/chat';
 
448
  } else {
449
  console.log('Token verification failed, clearing token');
450
  localStorage.removeItem('token');
 
451
  }
452
  } catch (error) {
453
  console.error('Error checking auth status:', error);
454
  localStorage.removeItem('token');
 
455
  }
456
  }
457
 
@@ -571,7 +574,6 @@
571
  errorMsg.classList.remove('hidden');
572
  return;
573
  }
574
- await checkAuthStatus();
575
  const urlParams = new URLSearchParams(window.location.search);
576
  const code = urlParams.get('code');
577
  const error = urlParams.get('error');
@@ -580,34 +582,41 @@
580
  errorMsg.classList.remove('hidden');
581
  console.error('OAuth error from URL:', error);
582
  } else if (code) {
583
- console.log('OAuth code detected in URL, processing callback');
584
- const provider = window.location.pathname.includes('google') ? 'google' : 'github';
585
- const callbackEndpoint = `/auth/${provider}/callback?code=${code}`;
586
- try {
587
- const response = await fetch(callbackEndpoint, {
588
- method: 'GET',
589
- headers: { 'Accept': 'application/json' }
590
- });
591
- if (response.ok) {
592
- const data = await response.json();
593
- if (data.access_token) {
594
- localStorage.setItem('token', data.access_token);
595
- console.log(`${provider} login successful, token saved`);
596
- window.location.href = '/chat';
 
 
 
 
 
 
 
597
  } else {
598
- throw new Error('No access token received');
 
 
 
599
  }
600
- } else {
601
- const errorData = await response.json();
602
- errorMsg.textContent = errorData.detail || `Failed to complete ${provider} login`;
603
  errorMsg.classList.remove('hidden');
604
- console.error(`Failed to complete ${provider} login:`, errorData);
605
  }
606
- } catch (error) {
607
- errorMsg.textContent = `Failed to process ${provider} login. Please try again.`;
608
- errorMsg.classList.remove('hidden');
609
- console.error(`Error processing ${provider} callback:`, error);
610
  }
 
 
611
  }
612
  });
613
 
 
433
  const token = localStorage.getItem('token');
434
  if (!token) {
435
  console.log('No token found in localStorage');
436
+ return false;
437
  }
438
  const response = await fetch('/api/verify-token', {
439
  method: 'GET',
 
445
  if (response.ok) {
446
  console.log('User is authenticated, redirecting to /chat');
447
  window.location.href = '/chat';
448
+ return true;
449
  } else {
450
  console.log('Token verification failed, clearing token');
451
  localStorage.removeItem('token');
452
+ return false;
453
  }
454
  } catch (error) {
455
  console.error('Error checking auth status:', error);
456
  localStorage.removeItem('token');
457
+ return false;
458
  }
459
  }
460
 
 
574
  errorMsg.classList.remove('hidden');
575
  return;
576
  }
 
577
  const urlParams = new URLSearchParams(window.location.search);
578
  const code = urlParams.get('code');
579
  const error = urlParams.get('error');
 
582
  errorMsg.classList.remove('hidden');
583
  console.error('OAuth error from URL:', error);
584
  } else if (code) {
585
+ console.log('OAuth code detected in URL, letting server handle callback');
586
+ // The server handles /auth/google/callback or /auth/github/callback
587
+ // Check auth status to detect the set cookie
588
+ const isAuthenticated = await checkAuthStatus();
589
+ if (!isAuthenticated) {
590
+ // Fetch the callback response to get the access_token
591
+ const provider = window.location.pathname.includes('google') ? 'google' : 'github';
592
+ try {
593
+ const response = await fetch(window.location.href, {
594
+ method: 'GET',
595
+ headers: { 'Accept': 'application/json' }
596
+ });
597
+ if (response.ok) {
598
+ const data = await response.json();
599
+ if (data.access_token) {
600
+ localStorage.setItem('token', data.access_token);
601
+ console.log(`${provider} login successful, token saved`);
602
+ window.location.href = '/chat';
603
+ } else {
604
+ throw new Error('No access token received');
605
+ }
606
  } else {
607
+ const errorData = await response.json();
608
+ errorMsg.textContent = errorData.detail || `Failed to complete ${provider} login`;
609
+ errorMsg.classList.remove('hidden');
610
+ console.error(`Failed to complete ${provider} login:`, errorData);
611
  }
612
+ } catch (error) {
613
+ errorMsg.textContent = `Failed to process ${provider} login. Please try again.`;
 
614
  errorMsg.classList.remove('hidden');
615
+ console.error(`Error processing ${provider} callback:`, error);
616
  }
 
 
 
 
617
  }
618
+ } else {
619
+ await checkAuthStatus();
620
  }
621
  });
622