summaryrefslogtreecommitdiff
path: root/scripts/setup_auth.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/setup_auth.py')
-rw-r--r--scripts/setup_auth.py129
1 files changed, 129 insertions, 0 deletions
diff --git a/scripts/setup_auth.py b/scripts/setup_auth.py
new file mode 100644
index 0000000..d675ddd
--- /dev/null
+++ b/scripts/setup_auth.py
@@ -0,0 +1,129 @@
+from __future__ import annotations
+
+import base64
+import json
+from datetime import datetime, UTC
+from getpass import getpass
+from pathlib import Path
+import shlex
+from urllib.parse import urlencode, quote
+
+import requests
+from dotenv import load_dotenv
+from jose import jwt
+import os
+
+
+ROOT_DIR = Path(__file__).resolve().parent.parent
+AUTH_FILE = ROOT_DIR / "backend" / "src" / "cache" / "auth.json"
+ENV_FILE = ROOT_DIR / ".env"
+REDIRECT_URI = "https://localhost/callback/"
+TOKEN_URL = "https://login.eveonline.com/v2/oauth/token"
+AUTH_URL = "https://login.eveonline.com/v2/oauth/authorize/"
+SCOPES = [
+ "esi-assets.read_assets.v1",
+ "esi-location.read_location.v1",
+ "esi-location.read_online.v1",
+ "esi-ui.open_window.v1",
+ "esi-ui.write_waypoint.v1",
+]
+
+
+def build_auth_url(client_id: str) -> str:
+ params = {
+ "response_type": "code",
+ "redirect_uri": REDIRECT_URI,
+ "client_id": client_id,
+ "state": "haul-local",
+ "scope": " ".join(SCOPES),
+ }
+ return f"{AUTH_URL}?{urlencode(params, quote_via=quote)}"
+
+
+def prompt_for_value(name: str, secret: bool = False) -> str:
+ while True:
+ value = getpass(f"{name}: ") if secret else input(f"{name}: ").strip()
+ if value:
+ return value
+ print(f"{name} is required.")
+
+
+def append_missing_env_values(values: dict[str, str]) -> None:
+ existing_text = ENV_FILE.read_text(encoding="utf-8") if ENV_FILE.exists() else ""
+ existing_keys = set()
+
+ for raw_line in existing_text.splitlines():
+ line = raw_line.strip()
+ if not line or line.startswith("#") or "=" not in line:
+ continue
+ key, _ = line.split("=", 1)
+ existing_keys.add(key.strip())
+
+ lines_to_add = []
+ for key, value in values.items():
+ if key not in existing_keys:
+ lines_to_add.append(f"{key}={shlex.quote(value)}")
+
+ if not lines_to_add:
+ return
+
+ if existing_text and not existing_text.endswith("\n"):
+ existing_text += "\n"
+
+ ENV_FILE.write_text(existing_text + "\n".join(lines_to_add) + "\n", encoding="utf-8")
+
+
+def main() -> int:
+ load_dotenv(ENV_FILE)
+
+ client_id = os.getenv("CLIENT_ID") or prompt_for_value("CLIENT_ID")
+ secret_key = os.getenv("SECRET_KEY") or prompt_for_value("SECRET_KEY", secret=True)
+
+ print("Open this URL in your browser and complete the EVE SSO flow:\n")
+ print(build_auth_url(client_id))
+ print("\nAfter the redirect, copy the 'code' query parameter and paste it below.")
+ code = input("Authorization code: ").strip()
+
+ if not code:
+ print("No authorization code provided.")
+ return 1
+
+ user_pass = f"{client_id}:{secret_key}".encode("utf-8")
+ headers = {
+ "Authorization": f"Basic {base64.urlsafe_b64encode(user_pass).decode()}",
+ "Content-Type": "application/x-www-form-urlencoded",
+ "Host": "login.eveonline.com",
+ }
+ form_values = {
+ "grant_type": "authorization_code",
+ "code": code,
+ }
+
+ response = requests.post(TOKEN_URL, data=form_values, headers=headers, timeout=30)
+ response.raise_for_status()
+ auth_data = response.json()
+ auth_data["expires_at"] = int(datetime.now(UTC).timestamp()) + int(auth_data.get("expires_in", 0))
+
+ AUTH_FILE.parent.mkdir(parents=True, exist_ok=True)
+ AUTH_FILE.write_text(json.dumps(auth_data, indent=2), encoding="utf-8")
+
+ claims = jwt.get_unverified_claims(auth_data["access_token"])
+ character_id = claims.get("sub", "").split(":")[-1] if claims.get("sub") else "unknown"
+ character_name = claims.get("name", "unknown")
+
+ append_missing_env_values(
+ {
+ "CLIENT_ID": client_id,
+ "SECRET_KEY": secret_key,
+ "character_id": character_id,
+ }
+ )
+
+ print(f"Saved auth cache to {AUTH_FILE}")
+ print(f"Detected character: {character_name} ({character_id})")
+ print(f"Ensured runtime env values exist in {ENV_FILE}")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main()) \ No newline at end of file