14809327fb
---ci--- project: acdl phase: 4 milestone: v1.28 status: execute persona: cli-engineer ---
58 lines
2.5 KiB
Python
58 lines
2.5 KiB
Python
"""nova auth login — session/PAT → OIDC token, store locally (REQ-344, C-7.3)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
from core.auth_store import store_credential, credentials_path
|
|
|
|
|
|
def _vend(pat: str, env: str, endpoint: str) -> dict:
|
|
"""Call the token-vend Lambda (locally or via the function URL)."""
|
|
if endpoint and endpoint.startswith("http"):
|
|
import urllib.request
|
|
body = json.dumps({"token": pat, "environment": env}).encode()
|
|
req = urllib.request.Request(endpoint, data=body, headers={"Content-Type": "application/json"})
|
|
with urllib.request.urlopen(req, timeout=30) as r:
|
|
return json.loads(r.read())
|
|
# Local: invoke the Lambda in-process.
|
|
import importlib.util
|
|
from pathlib import Path
|
|
p = Path(__import__("core").__file__).parent / "lambda" / "nova_idp_token_vend.py"
|
|
spec = importlib.util.spec_from_file_location("nova_idp_token_vend", p)
|
|
mod = importlib.util.module_from_spec(spec); spec.loader.exec_module(mod)
|
|
os.environ.setdefault("NOVA_LAMBDA_LOCAL_BYPASS", "1")
|
|
resp = mod.lambda_handler({"body": json.dumps({"token": pat, "environment": env})}, None)
|
|
return json.loads(resp["body"])
|
|
|
|
|
|
def add_parser(subparsers):
|
|
p = subparsers.add_parser("login", help="exchange a PAT/session for an OIDC token")
|
|
p.add_argument("--pat", default=None, help="PAT JWT (prompted if absent)")
|
|
p.add_argument("--session", default=None, help="session token (alias for --pat)")
|
|
p.add_argument("--environment", default="dev", help="target environment")
|
|
p.add_argument("--endpoint", default=os.environ.get("NOVA_TOKEN_VEND_URL", ""),
|
|
help="token-vend function URL (empty = local)")
|
|
p.set_defaults(_run=run)
|
|
|
|
|
|
def run(args) -> int:
|
|
pat = args.pat or args.session or os.environ.get("NOVA_PAT")
|
|
if not pat:
|
|
pat = sys.stdin.readline().strip()
|
|
if not pat:
|
|
print("error: no PAT/session provided", file=sys.stderr); return 1
|
|
result = _vend(pat, args.environment, args.endpoint)
|
|
if "token" not in result:
|
|
print(f"error: {result.get('error', result)}", file=sys.stderr); return 2
|
|
import base64
|
|
payload = json.loads(base64.urlsafe_b64decode(result["token"].split(".")[1] + "=="))
|
|
store_credential(
|
|
jti=payload.get("jti", ""), cred_type=payload.get("typ", "nova_oidc_token"),
|
|
exp=payload.get("exp", 0), oidc_token=result["token"],
|
|
)
|
|
print(f"logged in: jti={payload.get('jti')} exp={payload.get('exp')} "
|
|
f"file={credentials_path()}")
|
|
return 0 |