@@ -1464,5 +1464,137 @@ def post(self):
14641464 status = 'success' ,
14651465 message = f'Successfully sent { emails_sent } reminder emails' ,
14661466 total_users_processed = len (inactive_users ),
1467- errors = errors if errors else None
1468- ), 201
1467+ errors = errors if errors else None ),201
1468+
1469+
1470+ class GoogleOAuthView (Resource ):
1471+ def get (self ):
1472+ token_schema = UserSchema (partial = ("password" ),)
1473+
1474+ code = request .args .get ('code' )
1475+ if not code :
1476+ return dict (
1477+ status = 'fail' ,
1478+ message = 'No code received in query parameters'
1479+ ), 400
1480+
1481+
1482+ token_data = {
1483+ 'client_id' : current_app .config .get ('GOOGLE_CLIENT_ID' ),
1484+ 'client_secret' : current_app .config .get ('GOOGLE_CLIENT_SECRET' ),
1485+ 'code' : code ,
1486+ 'grant_type' : 'authorization_code' ,
1487+ 'redirect_uri' : current_app .config .get ('GOOGLE_REDIRECT_URI' ) ,
1488+ }
1489+
1490+ try :
1491+ token_response = requests .post (
1492+ url = 'https://oauth2.googleapis.com/token' ,
1493+ data = token_data ,
1494+ headers = {'Content-Type' : 'application/x-www-form-urlencoded' },
1495+ timeout = 10
1496+ )
1497+
1498+ except requests .RequestException as e :
1499+ return dict (status = 'fail' , message = "Failed to connect to Google OAuth" ), 500
1500+
1501+ if token_response .status_code != 200 :
1502+ try :
1503+ error_data = token_response .json ()
1504+ error_msg = error_data .get ('error_description' , error_data .get ('error' , 'Unknown error' ))
1505+ except :
1506+ error_msg = f"HTTP { token_response .status_code } : { token_response .text } "
1507+
1508+ return dict (
1509+ status = 'fail' ,
1510+ message = f"Token exchange failed: { error_msg } "
1511+ ), 401
1512+
1513+ try :
1514+ token_json = token_response .json ()
1515+ except ValueError :
1516+ return dict (status = 'fail' , message = "Invalid JSON response from Google" ), 500
1517+
1518+ if token_json .get ('error' ):
1519+ return dict (
1520+ status = 'fail' ,
1521+ message = f"Token error: { token_json .get ('error_description' , token_json ['error' ])} "
1522+ ), 401
1523+
1524+ access_token = token_json .get ('access_token' )
1525+ if not access_token :
1526+ return dict (status = 'fail' , message = "No access token received" ), 401
1527+
1528+ try :
1529+ user_response = requests .get (
1530+ url = 'https://www.googleapis.com/oauth2/v2/userinfo' ,
1531+ headers = {'Authorization' : f'Bearer { access_token } ' },
1532+ timeout = 10
1533+ )
1534+
1535+ except requests .RequestException as e :
1536+ return dict (status = 'fail' , message = "Failed to fetch user info" ), 500
1537+
1538+ if user_response .status_code != 200 :
1539+ return dict (status = 'fail' , message = "Failed to fetch user info" ), 401
1540+
1541+ try :
1542+ user_data = user_response .json ()
1543+ except ValueError :
1544+ return dict (status = 'fail' , message = "Invalid user data from Google" ), 500
1545+
1546+ email = user_data .get ('email' )
1547+ name = user_data .get ('name' )
1548+ verified_email = user_data .get ('verified_email' , False )
1549+
1550+ if not email :
1551+ return dict (status = 'fail' , message = "Email not provided by Google" ), 400
1552+
1553+ try :
1554+ user = User .find_first (email = email )
1555+
1556+ if not user :
1557+ user = User (
1558+ email = email ,
1559+ name = name ,
1560+ password = '' .join ((secrets .choice (string .ascii_letters )
1561+ for i in range (24 ))),
1562+ )
1563+ user .verified = verified_email
1564+
1565+ saved_user = user .save ()
1566+
1567+ if not saved_user :
1568+ return dict (status = 'fail' , message = 'Failed to create user' ), 500
1569+
1570+ user .name = name
1571+ user .verified = verified_email
1572+ updated_user = user .save ()
1573+
1574+ if not updated_user :
1575+ return dict (status = 'fail' , message = 'Failed to update user' ), 500
1576+
1577+ user_dict , errors = token_schema .dump (user )
1578+
1579+ if errors :
1580+ return dict (status = 'fail' , message = 'User serialization error' ), 500
1581+
1582+ access_token = user .generate_token (user_dict )
1583+
1584+ if not access_token :
1585+ return dict (status = 'fail' , message = "Failed to generate access token" ), 500
1586+
1587+ return dict (
1588+ status = 'success' ,
1589+ data = dict (
1590+ access_token = access_token ,
1591+ email = user .email ,
1592+ name = user .name ,
1593+ verified = user .verified ,
1594+ id = str (user .id ),
1595+ )
1596+ ), 200
1597+
1598+ except Exception as e :
1599+ return dict (status = 'fail' , message = 'Database error' ), 500
1600+
0 commit comments