68 lines
1.9 KiB
Python
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])
|