#!/usr/bin/env python3 """One-time setup: obtain a Nextcloud app password via Login Flow v2. Usage: python nc_login_flow.py https://your-nextcloud.example.com Writes the resulting server URL, login name and app password to credentials.json (used by server.py to authenticate against the Health API). """ import json import sys import time import webbrowser from pathlib import Path import requests CREDENTIALS_FILE = Path(__file__).parent / "credentials.json" # Shown to the user on Nextcloud's login-flow authorization page instead of the # default "python-requests/x.y.z". USER_AGENT = "PrivateHealthBridge" def run(base_url: str) -> None: base_url = base_url.rstrip("/") headers = {"User-Agent": USER_AGENT} resp = requests.post(f"{base_url}/index.php/login/v2", headers=headers, timeout=10) resp.raise_for_status() flow = resp.json() login_url = flow["login"] poll_token = flow["poll"]["token"] poll_endpoint = flow["poll"]["endpoint"] print(f"Open this URL and confirm the login in your browser:\n{login_url}\n") webbrowser.open(login_url) print("Waiting for confirmation...") while True: time.sleep(2) poll_resp = requests.post(poll_endpoint, data={"token": poll_token}, headers=headers, timeout=10) if poll_resp.status_code == 404: continue # not confirmed yet poll_resp.raise_for_status() result = poll_resp.json() break CREDENTIALS_FILE.write_text( json.dumps( { "server": result["server"].rstrip("/"), "login_name": result["loginName"], "app_password": result["appPassword"], }, indent=2, ) ) CREDENTIALS_FILE.chmod(0o600) print(f"Saved credentials to {CREDENTIALS_FILE}") if __name__ == "__main__": if len(sys.argv) != 2: print(f"Usage: {sys.argv[0]} ") sys.exit(1) run(sys.argv[1])