Skip to content

Commit e06420a

Browse files
cyberjunkyclaude
andcommitted
Fix Cloudflare SSO blocking with Safari TLS impersonation and multi-strategy login
Cloudflare blocks programmatic SSO login based on TLS fingerprinting. Safari impersonation via curl_cffi bypasses this where Chrome gets 429'd. - Add portal web login (/portal/api/login) as primary strategy with desktop browser headers — Garmin's own website uses this endpoint - Try multiple curl_cffi TLS impersonations: safari, safari_ios, chrome120, edge101, chrome (safari works best) - Fall through 429/connection errors to next strategy instead of failing - Add random browser UA generation via ua-generator to avoid fingerprinting - Use curl_cffi for DI token exchange and refresh (not just login) - Try both portal and mobile MFA endpoints with detailed error reporting - Proactively refresh DI tokens on load if expiring soon - Add ua-generator dependency to pyproject.toml Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 21aea2d commit e06420a

3 files changed

Lines changed: 361 additions & 20 deletions

File tree

garminconnect/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,17 @@ def login(self, /, tokenstore: str | None = None) -> tuple[str | None, str | Non
437437
)
438438
self.client.load(normalized_path)
439439
tokens_loaded = True
440+
441+
# Proactively refresh DI token if it's expired or about to expire.
442+
# This avoids hitting the SSO login endpoint (which may be
443+
# Cloudflare-blocked) when a simple DI refresh would suffice.
444+
if (
445+
self.client.di_refresh_token
446+
and self.client._token_expires_soon()
447+
):
448+
logger.debug("Token expiring soon, refreshing proactively")
449+
self.client._refresh_session()
450+
440451
except Exception as e:
441452
logger.debug(
442453
f"Failed to cleanly load tokens from {tokenstore}: {e}"

0 commit comments

Comments
 (0)