Files
private_health/bridge/nc_login_flow.py
T

68 lines
1.9 KiB
Python

#!/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]} <nextcloud-base-url>")
sys.exit(1)
run(sys.argv[1])