#!/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) expires_at_ts = garth.client.oauth2_token.expires_at 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": datetime.fromtimestamp(expires_at_ts).isoformat() } print("\n--- Copy everything below this line ---") print(json.dumps(tokens, indent=2)) print("--- Copy everything above this line ---") print(f"\nTokens expire: {tokens['expires_at']}")