5.6 KiB
description
| description |
|---|
| Specification for resolving garminconnect login failure and implementing garth MFA |
009: Fix Garmin Connect Login Failure and Implement Garth MFA
1. Problem Statement
The GarminSync backend service has been encountering persistent login failures with garminconnect due to recent changes in Garmin's Single Sign-On (SSO) process. The specific error observed in the logs is Login failed: Unexpected title: GARMIN Authentication Application. This issue prevents users from authenticating with Garmin Connect, especially those with Multi-Factor Authentication (MFA) enabled, severely impacting the service's core functionality. The existing implementation in backend/src/services/garmin_auth_service.py was relying on garminconnect's internal login method, which proved brittle against Garmin's evolving authentication flow.
2. Proposed Solution
The solution involves refactoring the authentication mechanism within the GarminAuthService to primarily leverage the garth library for direct login and robust MFA handling. garth is known for its resilience to Garmin's authentication changes and its explicit support for MFA flows. Once garth successfully establishes a session, garminconnect will implicitly pick up this session, thereby bypassing garminconnect's problematic internal login process.
3. Technical Details
3.1. Modified Files
backend/src/services/garmin_auth_service.py:- Imports: Replaced
garminconnect.Garminimport withgarthandgarth.exc.GarthException. _perform_loginmethod: Refactored to usegarth.Client().login(email=username, password=password). This method now returns agarth.Clientinstance and is responsible for initiating the coregarthlogin. It also raisesGarthExceptionif MFA is required, which is then handled by the calling method.initial_loginmethod: Modified to call the refactored_perform_login. It now handlesGarthExceptionto detect when MFA is required, returning a structured dictionary response ({"success": False, "mfa_required": True, ...}) to indicate the need for MFA input. The return type was updated fromOptional[GarminCredentials]toDict[str, Any].complete_mfa_loginmethod: A new asynchronous method added toGarminAuthService. This method takesusername,password, andmfa_code, and usesgarth.Client().login(email=username, password=password, mfa_token=mfa_code)to complete the MFA-enabled login. It returns structured dictionary responses for success or failure, includingGarminCredentialson successful authentication.GarminCredentialsInstantiation: Accesses todisplay_name,full_name, andunit_systemattributes are now directly from thegarth.Clientinstance (e.g.,client.display_name) rather than agarminconnect.Garmininstance, asgarthpopulates these.- Static Analysis Fixes: Corrected
typingimports to includeAnyandDict, and removed unusedTextIO. Suppressedmypyattr-definederrors forgarth.Clientattributes using# type: ignorecomments.
- Imports: Replaced
backend/src/api/garmin_sync.py: Sorted imports usingruff.
3.2. Authentication Flow Changes
The new authentication flow in the backend service is as follows:
- Initial Login Attempt: The
initial_loginmethod attempts a login usinggarth. - MFA Detection: If
garthdetects that MFA is required,initial_loginreturns a response indicating this, prompting the client (e.g., CLI) to request an MFA code from the user. - MFA Completion: The client then calls the
complete_mfa_loginmethod with the provided MFA code. This method attempts to finalize thegarthlogin. - Session Establishment: Upon successful login (either initial or after MFA),
garthautomatically manages the session tokens.garminconnect.Garmininstances, when initialized without credentials, will then implicitly use this establishedgarthsession.
4. Acceptance Criteria
4.1. Functional Requirements
- FR1: Users with Garmin Connect accounts (both with and without MFA enabled) shall be able to successfully authenticate with the GarminSync backend service.
- FR2: The
initial_loginendpoint/method shall correctly detect and indicate when MFA is required for a user account. - FR3: The
complete_mfa_loginendpoint/method shall successfully process a provided MFA code to complete the authentication for MFA-enabled accounts. - FR4: Upon successful authentication, the backend service shall return
GarminCredentialscontaining valid session and token information. - FR5: The
garminconnectlibrary, when used for subsequent API calls (e.g., fetching activities), shall successfully utilize the session established bygarthwithout requiring a separate login.
4.2. Quality Attributes
- QA1 - Robustness: The authentication flow shall be resilient to changes in Garmin's SSO page structure (as handled by
garth). - QA2 - Security: While
garmin_password_plaintextis still present, the change ensures the primary authentication usesgarth's secure methods. (Note: Removal of plaintext password storage is a future task). - QA3 - Maintainability: The code changes adhere to Python best practices and pass static analysis checks (
ruff,mypy).
5. Verification
The fix can be verified by deploying the updated backend service and attempting to log in with various Garmin Connect accounts, including those protected by MFA, using a compatible client (e.g., the CLI). Success is measured by the ability to authenticate and subsequently fetch data via garminconnect methods. Static analysis with ruff check and mypy passed.