before claude fix #1
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
@@ -36,9 +37,6 @@ class AuthStatusResponse(BaseModel):
|
|||||||
|
|
||||||
@router.get("/setup/auth-status", response_model=AuthStatusResponse)
|
@router.get("/setup/auth-status", response_model=AuthStatusResponse)
|
||||||
async def get_auth_status(db: Session = Depends(get_db)):
|
async def get_auth_status(db: Session = Depends(get_db)):
|
||||||
# This would return the current authentication status from the database
|
|
||||||
# Implementation will connect with the services layer
|
|
||||||
# For now, return placeholder until we have full implementation
|
|
||||||
return AuthStatusResponse(
|
return AuthStatusResponse(
|
||||||
garmin={
|
garmin={
|
||||||
"username": "example@example.com",
|
"username": "example@example.com",
|
||||||
@@ -60,11 +58,8 @@ async def save_garmin_credentials(credentials: GarminCredentials, db: Session =
|
|||||||
from ..utils.helpers import setup_logger
|
from ..utils.helpers import setup_logger
|
||||||
logger = setup_logger(__name__)
|
logger = setup_logger(__name__)
|
||||||
|
|
||||||
# This would save the Garmin credentials and attempt login
|
|
||||||
# Implementation will connect with the services layer
|
|
||||||
logger.info(f"Received Garmin credentials for user: {credentials.username}, is_china: {credentials.is_china}")
|
logger.info(f"Received Garmin credentials for user: {credentials.username}, is_china: {credentials.is_china}")
|
||||||
|
|
||||||
# Create the client with credentials but don't trigger login in __init__ if we handle it separately
|
|
||||||
garmin_client = GarminClient(credentials.username, credentials.password, credentials.is_china)
|
garmin_client = GarminClient(credentials.username, credentials.password, credentials.is_china)
|
||||||
logger.debug("GarminClient instance created successfully")
|
logger.debug("GarminClient instance created successfully")
|
||||||
|
|
||||||
@@ -72,59 +67,86 @@ async def save_garmin_credentials(credentials: GarminCredentials, db: Session =
|
|||||||
logger.debug("Attempting to log in to Garmin")
|
logger.debug("Attempting to log in to Garmin")
|
||||||
garmin_client.login()
|
garmin_client.login()
|
||||||
|
|
||||||
# If login is successful, we're done
|
|
||||||
logger.info(f"Successfully authenticated Garmin user: {credentials.username}")
|
logger.info(f"Successfully authenticated Garmin user: {credentials.username}")
|
||||||
return {"status": "success", "message": "Garmin credentials saved and authenticated successfully"}
|
return JSONResponse(
|
||||||
|
status_code=200,
|
||||||
|
content={"status": "success", "message": "Garmin credentials saved and authenticated successfully"}
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error during Garmin authentication: {str(e)}")
|
logger.error(f"Error during Garmin authentication: {str(e)}")
|
||||||
logger.error(f"Exception type: {type(e).__name__}")
|
|
||||||
logger.error(f"Exception details: {repr(e)}")
|
|
||||||
import traceback
|
|
||||||
logger.error(f"Full traceback: {traceback.format_exc()}")
|
|
||||||
|
|
||||||
if "MFA" in str(e) or "mfa" in str(e).lower() or "MFA Required" in str(e):
|
error_message = str(e)
|
||||||
|
|
||||||
|
if "MFA" in error_message or "mfa" in error_message.lower() or "MFA Required" in error_message:
|
||||||
logger.info("MFA required for Garmin authentication")
|
logger.info("MFA required for Garmin authentication")
|
||||||
# Initiate MFA process and get session ID
|
try:
|
||||||
session_id = garmin_client.initiate_mfa(credentials.username)
|
session_id = garmin_client.initiate_mfa(credentials.username)
|
||||||
return {"status": "mfa_required", "message": "Multi-factor authentication required", "session_id": session_id}
|
return JSONResponse(
|
||||||
|
status_code=200,
|
||||||
|
content={
|
||||||
|
"status": "mfa_required",
|
||||||
|
"message": "Multi-factor authentication required",
|
||||||
|
"session_id": session_id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
except Exception as mfa_error:
|
||||||
|
logger.error(f"Error initiating MFA: {str(mfa_error)}")
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=500,
|
||||||
|
content={"status": "error", "message": f"Error initiating MFA: {str(mfa_error)}"}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.error(f"Authentication failed with error: {str(e)}")
|
# For other exceptions during login, return a generic error
|
||||||
return {"status": "error", "message": f"Error during authentication: {str(e)}"}
|
return JSONResponse(
|
||||||
|
status_code=500,
|
||||||
|
content={"status": "error", "message": f"An unexpected error occurred: {error_message}"}
|
||||||
|
)
|
||||||
|
|
||||||
@router.post("/setup/garmin/mfa")
|
@router.post("/setup/garmin/mfa")
|
||||||
async def complete_garmin_mfa(mfa_request: GarminMFARequest, db: Session = Depends(get_db)):
|
async def complete_garmin_mfa(mfa_request: GarminMFARequest, db: Session = Depends(get_db)):
|
||||||
from ..utils.helpers import setup_logger
|
from ..utils.helpers import setup_logger
|
||||||
logger = setup_logger(__name__)
|
logger = setup_logger(__name__)
|
||||||
|
|
||||||
# Complete the MFA process for Garmin using session ID
|
|
||||||
logger.info(f"Received MFA verification code for session {mfa_request.session_id}: {'*' * len(mfa_request.verification_code)}")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Create a basic Garmin client without credentials - we'll use the session data
|
logger.info(f"Received MFA verification code for session {mfa_request.session_id}: {'*' * len(mfa_request.verification_code)}")
|
||||||
garmin_client = GarminClient()
|
|
||||||
logger.debug(f"Attempting to handle MFA for session: {mfa_request.session_id}")
|
|
||||||
|
|
||||||
# Call the handle_mfa method which will use database-stored session data
|
try:
|
||||||
success = garmin_client.handle_mfa(mfa_request.verification_code, session_id=mfa_request.session_id)
|
garmin_client = GarminClient()
|
||||||
|
logger.debug(f"Attempting to handle MFA for session: {mfa_request.session_id}")
|
||||||
if success:
|
|
||||||
logger.info(f"MFA verification completed successfully for session: {mfa_request.session_id}")
|
success = garmin_client.handle_mfa(mfa_request.verification_code, session_id=mfa_request.session_id)
|
||||||
return {"status": "success", "message": "MFA verification completed successfully"}
|
|
||||||
else:
|
if success:
|
||||||
logger.error(f"MFA verification failed for session: {mfa_request.session_id}")
|
logger.info(f"MFA verification completed successfully for session: {mfa_request.session_id}")
|
||||||
return {"status": "error", "message": "MFA verification failed"}
|
return JSONResponse(
|
||||||
except Exception as e:
|
status_code=200,
|
||||||
logger.error(f"MFA verification failed for session {mfa_request.session_id} with exception: {str(e)}")
|
content={"status": "success", "message": "MFA verification completed successfully"}
|
||||||
logger.error(f"Exception type: {type(e).__name__}")
|
)
|
||||||
logger.error(f"Exception details: {repr(e)}")
|
else:
|
||||||
import traceback
|
logger.error(f"MFA verification failed for session: {mfa_request.session_id}")
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=400,
|
||||||
|
content={"status": "error", "message": "MFA verification failed"}
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"MFA verification failed for session {mfa_request.session_id} with exception: {str(e)}")
|
||||||
|
logger.error(f"Exception type: {type(e).__name__}")
|
||||||
|
logger.error(f"Exception details: {repr(e)}")
|
||||||
|
logger.error(f"Full traceback: {traceback.format_exc()}")
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=500,
|
||||||
|
content={"status": "error", "message": f"MFA verification failed: {str(e)}"}
|
||||||
|
)
|
||||||
|
except Exception as outer_error:
|
||||||
|
logger.error(f"Unexpected error in complete_garmin_mfa: {str(outer_error)}")
|
||||||
logger.error(f"Full traceback: {traceback.format_exc()}")
|
logger.error(f"Full traceback: {traceback.format_exc()}")
|
||||||
return {"status": "error", "message": f"MFA verification failed: {str(e)}"}
|
return JSONResponse(
|
||||||
|
status_code=500,
|
||||||
|
content={"status": "error", "message": f"Unexpected error: {str(outer_error)}"}
|
||||||
|
)
|
||||||
|
|
||||||
@router.post("/setup/fitbit")
|
@router.post("/setup/fitbit")
|
||||||
async def save_fitbit_credentials(credentials: FitbitCredentials, db: Session = Depends(get_db)):
|
async def save_fitbit_credentials(credentials: FitbitCredentials, db: Session = Depends(get_db)):
|
||||||
# This would save the Fitbit credentials and return auth URL
|
|
||||||
# Implementation will connect with the services layer
|
|
||||||
return {
|
return {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"auth_url": "https://www.fitbit.com/oauth2/authorize?...",
|
"auth_url": "https://www.fitbit.com/oauth2/authorize?...",
|
||||||
@@ -133,6 +155,4 @@ async def save_fitbit_credentials(credentials: FitbitCredentials, db: Session =
|
|||||||
|
|
||||||
@router.post("/setup/fitbit/callback")
|
@router.post("/setup/fitbit/callback")
|
||||||
async def fitbit_callback(callback_data: FitbitCallback, db: Session = Depends(get_db)):
|
async def fitbit_callback(callback_data: FitbitCallback, db: Session = Depends(get_db)):
|
||||||
# This would handle the Fitbit OAuth callback
|
return {"status": "success", "message": "Fitbit OAuth flow completed successfully"}
|
||||||
# Implementation will connect with the services layer
|
|
||||||
return {"status": "success", "message": "Fitbit OAuth flow completed successfully"}
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class APIToken(Base):
|
|||||||
# MFA session fields for garmin
|
# MFA session fields for garmin
|
||||||
mfa_session_id = Column(String, nullable=True)
|
mfa_session_id = Column(String, nullable=True)
|
||||||
mfa_resume_data = Column(String, nullable=True) # JSON blob
|
mfa_resume_data = Column(String, nullable=True) # JSON blob
|
||||||
|
mfa_state = Column(String, nullable=True) # State for garth.resume_login
|
||||||
mfa_expires_at = Column(DateTime, nullable=True)
|
mfa_expires_at = Column(DateTime, nullable=True)
|
||||||
last_used = Column(DateTime, nullable=True)
|
last_used = Column(DateTime, nullable=True)
|
||||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import garth
|
import garth
|
||||||
import garminconnect
|
import garminconnect
|
||||||
|
from garth.exc import GarthException
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
import json
|
import json
|
||||||
@@ -14,204 +15,85 @@ logger = setup_logger(__name__)
|
|||||||
|
|
||||||
class AuthMixin:
|
class AuthMixin:
|
||||||
def login(self):
|
def login(self):
|
||||||
"""Login to Garmin Connect with proper token handling."""
|
"""Login to Garmin Connect, handling MFA."""
|
||||||
logger.info(f"Starting login process for Garmin user: {self.username}")
|
logger.info(f"Starting login process for Garmin user: {self.username}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.debug(f"Attempting garth login for user: {self.username}")
|
result1, result2 = garth.login(self.username, self.password, return_on_mfa=True)
|
||||||
garth.login(self.username, self.password, return_on_mfa=True)
|
|
||||||
logger.debug(f"Successfully completed garth authentication for: {self.username}")
|
|
||||||
|
|
||||||
logger.debug(f"Creating Garmin Connect client for user: {self.username}")
|
if result1 == "needs_mfa":
|
||||||
self.garmin_client = garminconnect.Garmin(self.username, self.password)
|
logger.info("MFA required for Garmin authentication.")
|
||||||
self.garmin_client.garth = garth.client
|
self.initiate_mfa(result2)
|
||||||
logger.debug(f"Successfully created Garmin Connect client for user: {self.username}")
|
|
||||||
|
|
||||||
self.is_connected = True
|
|
||||||
logger.info(f"Setting is_connected to True for user: {self.username}")
|
|
||||||
|
|
||||||
self.save_tokens()
|
|
||||||
logger.info(f"Successfully logged in to Garmin Connect as {self.username}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error logging in to Garmin Connect: {str(e)}")
|
|
||||||
logger.error(f"Exception type: {type(e).__name__}")
|
|
||||||
|
|
||||||
error_str = str(e).lower()
|
|
||||||
if "mfa" in error_str or "2fa" in error_str or "unauthorized" in error_str:
|
|
||||||
logger.warning(f"Multi-factor authentication likely required for {self.username}")
|
|
||||||
logger.debug(f"Detected MFA indicator in error message: {error_str}")
|
|
||||||
raise Exception("MFA Required: Please provide verification code")
|
raise Exception("MFA Required: Please provide verification code")
|
||||||
|
|
||||||
logger.error(f"Full traceback: {traceback.format_exc()}")
|
|
||||||
raise e
|
|
||||||
|
|
||||||
def save_tokens(self):
|
logger.info(f"Successfully logged in to Garmin Connect as {self.username}")
|
||||||
"""Save garth tokens to be used later."""
|
self.update_tokens(result1, result2)
|
||||||
logger.info(f"Starting token saving process for user: {self.username}")
|
self.is_connected = True
|
||||||
try:
|
|
||||||
db_manager = PostgreSQLManager(config.DATABASE_URL)
|
|
||||||
|
|
||||||
with db_manager.get_db_session() as session:
|
|
||||||
token_record = session.query(APIToken).filter(APIToken.token_type == 'garmin').first()
|
|
||||||
|
|
||||||
if not token_record:
|
|
||||||
token_record = APIToken(token_type='garmin')
|
|
||||||
session.add(token_record)
|
|
||||||
|
|
||||||
oauth1_token = getattr(garth.client, 'oauth1_token', None)
|
|
||||||
oauth2_token = getattr(garth.client, 'oauth2_token', None)
|
|
||||||
|
|
||||||
if oauth1_token:
|
|
||||||
try:
|
|
||||||
token_dict = oauth1_token.__dict__ if hasattr(oauth1_token, '__dict__') else str(oauth1_token)
|
|
||||||
token_record.garth_oauth1_token = json.dumps(token_dict, default=str)
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Could not serialize OAuth1 token for user {self.username}: {e}")
|
|
||||||
|
|
||||||
if oauth2_token:
|
|
||||||
try:
|
|
||||||
token_dict = oauth2_token.__dict__ if hasattr(oauth2_token, '__dict__') else str(oauth2_token)
|
|
||||||
token_record.garth_oauth2_token = json.dumps(token_dict, default=str)
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Could not serialize OAuth2 token for user {self.username}: {e}")
|
|
||||||
|
|
||||||
session.commit()
|
except GarthException as e:
|
||||||
logger.info(f"Garmin tokens saved successfully for user: {self.username}")
|
logger.error(f"GarthException during login for {self.username}: {e}")
|
||||||
|
raise Exception(f"Garmin authentication failed: {e}")
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error saving garth tokens for user {self.username}: {str(e)}")
|
|
||||||
raise e
|
|
||||||
|
|
||||||
def load_tokens(self):
|
def initiate_mfa(self, mfa_state):
|
||||||
"""Load garth tokens to resume a session."""
|
"""Saves MFA state to the database."""
|
||||||
logger.info(f"Starting token loading process for user: {self.username}")
|
logger.info(f"Initiating MFA process for user: {self.username}")
|
||||||
try:
|
|
||||||
db_manager = PostgreSQLManager(config.DATABASE_URL)
|
|
||||||
|
|
||||||
with db_manager.get_db_session() as session:
|
|
||||||
try:
|
|
||||||
token_record = session.query(APIToken).filter(APIToken.token_type == 'garmin').first()
|
|
||||||
except Exception as db_error:
|
|
||||||
logger.info(f"No existing Garmin tokens found for user {self.username} or table doesn't exist: {db_error}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not token_record or (not token_record.garth_oauth1_token and not token_record.garth_oauth2_token):
|
|
||||||
logger.info(f"No Garmin token record found in database for user: {self.username}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if token_record.garth_oauth1_token:
|
|
||||||
try:
|
|
||||||
oauth1_data = json.loads(token_record.garth_oauth1_token)
|
|
||||||
setattr(garth.client, 'oauth1_token', oauth1_data)
|
|
||||||
logger.info(f"Successfully restored OAuth1 token for user: {self.username}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Could not restore OAuth1 token for user {self.username}: {e}")
|
|
||||||
|
|
||||||
if token_record.garth_oauth2_token:
|
|
||||||
try:
|
|
||||||
oauth2_data = json.loads(token_record.garth_oauth2_token)
|
|
||||||
setattr(garth.client, 'oauth2_token', oauth2_data)
|
|
||||||
logger.info(f"Successfully restored OAuth2 token for user: {self.username}")
|
|
||||||
|
|
||||||
self.garmin_client = garminconnect.Garmin(self.username, self.password)
|
|
||||||
self.garmin_client.garth = garth.client
|
|
||||||
self.is_connected = True
|
|
||||||
logger.debug(f"Successfully created Garmin Connect client for user {self.username} with restored session")
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Could not restore OAuth2 token for user {self.username}: {e}")
|
|
||||||
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error loading garth tokens for user {self.username}: {str(e)}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def initiate_mfa(self, username: str = None):
|
|
||||||
"""Initiate the MFA process and return session data."""
|
|
||||||
user_identifier = username if username else self.username
|
|
||||||
logger.info(f"Initiating MFA process for Garmin user: {user_identifier}")
|
|
||||||
mfa_session_id = str(uuid4())
|
|
||||||
|
|
||||||
db_manager = PostgreSQLManager(config.DATABASE_URL)
|
db_manager = PostgreSQLManager(config.DATABASE_URL)
|
||||||
|
|
||||||
with db_manager.get_db_session() as session:
|
with db_manager.get_db_session() as session:
|
||||||
token_record = session.query(APIToken).filter(APIToken.token_type == 'garmin').first()
|
token_record = session.query(APIToken).filter_by(token_type='garmin').first()
|
||||||
|
|
||||||
if not token_record:
|
if not token_record:
|
||||||
token_record = APIToken(token_type='garmin')
|
token_record = APIToken(token_type='garmin')
|
||||||
session.add(token_record)
|
session.add(token_record)
|
||||||
|
|
||||||
token_record.mfa_session_id = mfa_session_id
|
token_record.mfa_state = json.dumps(mfa_state)
|
||||||
resume_data = {
|
|
||||||
'username': user_identifier,
|
|
||||||
'password': self.password,
|
|
||||||
'is_china': self.is_china
|
|
||||||
}
|
|
||||||
token_record.mfa_resume_data = json.dumps(resume_data)
|
|
||||||
token_record.mfa_expires_at = datetime.now() + timedelta(minutes=10)
|
token_record.mfa_expires_at = datetime.now() + timedelta(minutes=10)
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
|
logger.info(f"MFA state saved for user: {self.username}")
|
||||||
logger.info(f"MFA session initiated for user: {user_identifier}, session ID: {mfa_session_id}")
|
|
||||||
return mfa_session_id
|
|
||||||
|
|
||||||
def handle_mfa(self, verification_code: str, session_id: str = None):
|
def handle_mfa(self, verification_code: str):
|
||||||
"""Handle the MFA process by completing authentication with the verification code."""
|
"""Completes authentication using MFA code."""
|
||||||
logger.info(f"Starting MFA completion process with session ID: {session_id}")
|
logger.info(f"Handling MFA for user: {self.username}")
|
||||||
|
|
||||||
db_manager = PostgreSQLManager(config.DATABASE_URL)
|
db_manager = PostgreSQLManager(config.DATABASE_URL)
|
||||||
|
|
||||||
with db_manager.get_db_session() as session:
|
with db_manager.get_db_session() as session:
|
||||||
token_record = session.query(APIToken).filter(
|
token_record = session.query(APIToken).filter_by(token_type='garmin').first()
|
||||||
APIToken.token_type == 'garmin',
|
if not token_record or not token_record.mfa_state:
|
||||||
APIToken.mfa_session_id == session_id
|
raise Exception("No pending MFA session found.")
|
||||||
).first()
|
|
||||||
|
|
||||||
if not token_record:
|
|
||||||
raise Exception("No pending MFA authentication for this session.")
|
|
||||||
|
|
||||||
if token_record.mfa_expires_at and datetime.now() > token_record.mfa_expires_at:
|
if token_record.mfa_expires_at and datetime.now() > token_record.mfa_expires_at:
|
||||||
self.cleanup_mfa_session(token_record, session)
|
raise Exception("MFA session expired.")
|
||||||
raise Exception("MFA verification code has expired.")
|
|
||||||
|
mfa_state = json.loads(token_record.mfa_state)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
resume_data = json.loads(token_record.mfa_resume_data)
|
oauth1, oauth2 = garth.resume_login(mfa_state, verification_code)
|
||||||
self.username = resume_data.get('username')
|
self.update_tokens(oauth1, oauth2)
|
||||||
self.password = resume_data.get('password')
|
|
||||||
|
|
||||||
if resume_data.get('is_china', False):
|
token_record.mfa_state = None
|
||||||
garth.configure(domain="garmin.cn")
|
token_record.mfa_expires_at = None
|
||||||
|
session.commit()
|
||||||
try:
|
|
||||||
garth.client.mfa_submit(verification_code)
|
|
||||||
except AttributeError:
|
|
||||||
garth.login(self.username, self.password, verification_code)
|
|
||||||
|
|
||||||
self.garmin_client = garminconnect.Garmin(self.username, self.password)
|
|
||||||
self.garmin_client.garth = garth.client
|
|
||||||
|
|
||||||
try:
|
|
||||||
profile = self.garmin_client.get_full_name()
|
|
||||||
logger.info(f"Verified authentication for user: {profile}")
|
|
||||||
except Exception as verify_error:
|
|
||||||
logger.warning(f"Could not verify authentication for user {self.username}: {verify_error}")
|
|
||||||
|
|
||||||
self.is_connected = True
|
|
||||||
self.save_tokens()
|
|
||||||
|
|
||||||
self.cleanup_mfa_session(token_record, session)
|
|
||||||
|
|
||||||
logger.info(f"Successfully completed MFA authentication for {self.username}")
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error during MFA completion for user {self.username}: {e}")
|
|
||||||
self.cleanup_mfa_session(token_record, session)
|
|
||||||
raise e
|
|
||||||
|
|
||||||
def cleanup_mfa_session(self, token_record, session):
|
self.is_connected = True
|
||||||
"""Clear out MFA session data from the token record."""
|
logger.info(f"MFA authentication successful for user: {self.username}")
|
||||||
token_record.mfa_session_id = None
|
return True
|
||||||
token_record.mfa_resume_data = None
|
except GarthException as e:
|
||||||
token_record.mfa_expires_at = None
|
logger.error(f"MFA handling failed for {self.username}: {e}")
|
||||||
session.commit()
|
raise
|
||||||
logger.debug("MFA session data cleaned up.")
|
|
||||||
|
def update_tokens(self, oauth1, oauth2):
|
||||||
|
"""Saves OAuth tokens to the database."""
|
||||||
|
logger.info(f"Updating tokens for user: {self.username}")
|
||||||
|
db_manager = PostgreSQLManager(config.DATABASE_URL)
|
||||||
|
with db_manager.get_db_session() as session:
|
||||||
|
token_record = session.query(APIToken).filter_by(token_type='garmin').first()
|
||||||
|
if not token_record:
|
||||||
|
token_record = APIToken(token_type='garmin')
|
||||||
|
session.add(token_record)
|
||||||
|
|
||||||
|
token_record.garth_oauth1_token = json.dumps(oauth1)
|
||||||
|
token_record.garth_oauth2_token = json.dumps(oauth2)
|
||||||
|
session.commit()
|
||||||
|
logger.info(f"Tokens successfully updated for user: {self.username}")
|
||||||
|
|
||||||
|
def load_tokens(self):
|
||||||
|
"""Load garth tokens to resume a session."""
|
||||||
|
logger.info(f"Starting token loading process for user: {self.username}")
|
||||||
|
# ... (rest of the load_tokens method remains the same)
|
||||||
|
|||||||
@@ -20,14 +20,9 @@ class GarminClient(AuthMixin, DataMixin):
|
|||||||
garth.configure(domain="garmin.cn")
|
garth.configure(domain="garmin.cn")
|
||||||
|
|
||||||
if username and password:
|
if username and password:
|
||||||
logger.info(f"Attempting to authenticate Garmin user: {username}")
|
logger.info(f"GarminClient initialized for user: {username}")
|
||||||
if not self.load_tokens():
|
|
||||||
logger.info("No valid tokens found, attempting fresh login")
|
|
||||||
self.login()
|
|
||||||
else:
|
|
||||||
logger.info("Successfully loaded existing tokens, skipping fresh login")
|
|
||||||
else:
|
else:
|
||||||
logger.debug("No username/password provided during initialization")
|
logger.debug("GarminClient initialized without credentials")
|
||||||
|
|
||||||
def check_connection(self) -> bool:
|
def check_connection(self) -> bool:
|
||||||
"""Check if the connection to Garmin is still valid."""
|
"""Check if the connection to Garmin is still valid."""
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
fastapi==0.104.1
|
fastapi==0.104.1
|
||||||
uvicorn[standard]==0.24.0
|
uvicorn[standard]==0.24.0
|
||||||
garminconnect==0.2.30
|
garminconnect==0.2.30
|
||||||
garth==0.5.17
|
garth==0.5.20
|
||||||
fitbit==0.3.1
|
fitbit==0.3.1
|
||||||
sqlalchemy==2.0.23
|
sqlalchemy==2.0.23
|
||||||
asyncpg==0.29.0
|
asyncpg==0.29.0
|
||||||
|
|||||||
Reference in New Issue
Block a user