Files
phaseflow/scripts/garmin_auth.py
Petru Paler ca35b36efa Fix garth token serialization using TypeAdapter for Pydantic dataclasses
Garth's OAuth1Token and OAuth2Token are Pydantic dataclasses, not BaseModel
subclasses, so they require TypeAdapter for serialization instead of model_dump().
Also adds user-friendly error handling for authentication failures.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 17:45:01 +00:00

58 lines
1.7 KiB
Python

#!/usr/bin/env python3
# ABOUTME: PhaseFlow Garmin Token Generator using garth library.
# ABOUTME: Run this locally to authenticate with Garmin (supports MFA).
"""
PhaseFlow Garmin Token Generator
Run this locally to authenticate with Garmin (supports MFA)
Usage:
pip install garth
python3 garmin_auth.py
"""
import json
import sys
from datetime import datetime
from getpass import getpass
try:
import garth
from garth.auth_tokens import OAuth1Token, OAuth2Token
from garth.exc import GarthHTTPError
from pydantic import TypeAdapter
except ImportError:
print("Error: garth library not installed.")
print("Please install it with: pip install garth")
exit(1)
email = input("Garmin email: ")
password = getpass("Garmin password: ")
# MFA handled automatically - prompts if needed
try:
garth.login(email, password)
except GarthHTTPError as e:
if "401" in str(e):
print("\nError: Invalid email or password.", file=sys.stderr)
else:
print(f"\nError: Authentication failed - {e}", file=sys.stderr)
exit(1)
except Exception as e:
print(f"\nError: {e}", file=sys.stderr)
exit(1)
# Serialize Pydantic dataclasses using TypeAdapter
oauth1_adapter = TypeAdapter(OAuth1Token)
oauth2_adapter = TypeAdapter(OAuth2Token)
tokens = {
"oauth1": oauth1_adapter.dump_python(garth.client.oauth1_token, mode='json'),
"oauth2": oauth2_adapter.dump_python(garth.client.oauth2_token, mode='json'),
"expires_at": garth.client.oauth2_token.expires_at
}
print("\n--- Copy everything below this line ---")
print(json.dumps(tokens, indent=2))
print("--- Copy everything above this line ---")
expires_dt = datetime.fromtimestamp(tokens['expires_at'])
print(f"\nTokens expire: {expires_dt.isoformat()}")