github cyberjunky/python-garminconnect 0.3.5
Release 0.3.5

5 hours ago

What's Changed

This release includes a security fix (please upgrade), major login
resilience improvements, two new activity-editing methods, and bug fixes.

๐Ÿ”’ Security

  • Token store hardening (GHSA-wjhr-76vg-2hvc, CWE-732, High). Client.dump()
    previously wrote garmin_tokens.json under the process umask, leaving it
    world-readable (0o644) on the default Linux umask. The file holds the DI
    refresh token, so any local user on a shared host could read it and gain
    persistent access to the account. Tokens are now written 0o600 inside a
    0o700 directory (with O_NOFOLLOW and a defensive chmod), regardless of
    umask. Affected: โ‰ค 0.3.4. Patched: 0.3.5. Upgrading is strongly advised
    for anyone storing tokens on a multi-user system.

โœจ Login resilience (fixes #369)

  • In-chain token validation. Each login strategy's token is now verified
    against the API before the chain accepts it. A token the API rejects
    (401/403 โ€” an account/region-specific condition) is discarded and the next
    strategy is tried automatically. Only definitive auth rejections fall
    through; transient 5xx/network errors never block a working login.
  • Self-healing from poisoned cached tokens. If cached tokens load but the
    API rejects them, they're discarded and a fresh credential login runs
    automatically โ€” fixing the long-standing footgun where a stale token cache
    silently short-circuited the login chain on every run.
  • logout() is now functional (was a deprecated no-op): clears in-memory
    auth state and removes cached tokens on disk. Callable as g.logout() or
    g.logout(tokenstore).
  • New verify_login option (Garmin(..., verify_login=False)) to restore
    the legacy "first token wins" behavior.
  • New skip_strategies on the client to force or skip specific login
    strategies โ€” useful for diagnosing which auth path works on your account.
  • Cleaner failure handling: explicit Cloudflare 403 / CAPTCHA detection,
    child/family-account detection, 429 errors preserved through the login
    wrapper, and no more stack-trace dumps for expected login failures.

๐Ÿ†• New API methods

  • set_activity_description(activity_id, description) (#367)
  • set_activity_exercise_sets(activity_id, payload) (#368)
  • CN accounts: domain-aware service URLs for authentication (#366)

Both new methods are available in the interactive demo under the new
โœ๏ธ Activity Editing menu.

๐Ÿ› Fixes

  • get_training_readiness now correctly annotated list[dict[str, Any]] โ€” the
    endpoint returns a list of snapshots, not a single dict (#361). Downstream
    tooling that validates against the return type (e.g. pydantic-based MCP
    servers) no longer breaks.
  • get_morning_training_readiness keeps defensive handling for a single-dict
    response.
  • Typed wrapper: typed.get_training_readiness() normalizes an empty response
    to [].

๐Ÿงช Internal / tooling

  • New mocked test suites for login recovery and token-store permissions; no
    network required.
  • test_strategy.py โ€” interactive diagnostic to run each login strategy in
    isolation and tee output to a log.

Upgrade

pip install --upgrade garminconnect

Full Changelog: 0.3.4...0.3.5

Don't miss a new python-garminconnect release

NewReleases is sending notifications on new releases.