diff --git a/app.py b/app.py index ee3f538..e08a2d0 100644 --- a/app.py +++ b/app.py @@ -11,7 +11,7 @@ import sys from contextlib import asynccontextmanager from datetime import datetime -from fastapi import FastAPI, Request, Form, UploadFile, File +from fastapi import FastAPI, Request, Form, UploadFile, File, BackgroundTasks from fastapi.responses import HTMLResponse, RedirectResponse, FileResponse, JSONResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates @@ -859,6 +859,80 @@ async def delete_firmware(filename: str): return RedirectResponse("/devices", status_code=303) +# ── Azure Auth (Web-basiert) ────────────────────────────────────────────── + +# Global state for device code flow +_auth_state = {"status": "idle", "user_code": None, "verification_uri": None, "error": None} + + +def _run_device_code_auth(): + """Background task: run device code flow and save auth record.""" + from azure.identity import DeviceCodeCredential, TokenCachePersistenceOptions + from database import get_setting, set_setting + + client_id = get_setting("azure_client_id") + tenant_id = get_setting("azure_auth_tenant", "common") + scopes = get_setting("azure_scopes", "User.Read").split() + + if not client_id: + _auth_state["status"] = "error" + _auth_state["error"] = "Anwendungs-ID (Client) nicht konfiguriert" + return + + def prompt_callback(verification_uri, user_code, expires_on): + _auth_state["user_code"] = user_code + _auth_state["verification_uri"] = verification_uri + _auth_state["status"] = "waiting" + + try: + cred = DeviceCodeCredential( + client_id=client_id, + tenant_id=tenant_id, + cache_persistence_options=TokenCachePersistenceOptions( + name="busylight", allow_unencrypted_storage=True + ), + prompt_callback=prompt_callback, + ) + record = cred.authenticate(tenant_id=tenant_id, scopes=scopes) + set_setting("azure_auth_record", record.serialize()) + _auth_state["status"] = "success" + + # Reinit graph client + try: + scheduler.graph = GraphAPI() + logger.info("Graph-Client nach Web-Auth neu initialisiert") + except Exception as e: + logger.error("Graph-Client Reinit fehlgeschlagen: %s", e) + + except Exception as e: + _auth_state["status"] = "error" + _auth_state["error"] = str(e) + logger.error("Web-Auth fehlgeschlagen: %s", e) + + +@app.post("/api/azure/auth") +async def start_azure_auth(): + """Start device code flow in background.""" + import threading + _auth_state["status"] = "starting" + _auth_state["user_code"] = None + _auth_state["verification_uri"] = None + _auth_state["error"] = None + + # Clear old auth record + database.set_setting("azure_auth_record", "") + + thread = threading.Thread(target=_run_device_code_auth, daemon=True) + thread.start() + return JSONResponse({"status": "starting"}) + + +@app.get("/api/azure/auth/status") +async def azure_auth_status(): + """Poll auth state from frontend.""" + return JSONResponse(_auth_state) + + # ── CLI Auth Command ─────────────────────────────────────────────────────── def cli_auth(): diff --git a/templates/settings.html b/templates/settings.html index c24add9..fa11ccf 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -43,19 +43,53 @@ placeholder="User.Read User.Read.All Presence.Read Presence.Read.All"> Standard: User.Read User.Read.All Presence.Read Presence.Read.All -
- Fuehre auf dem Server aus:
- python app.py --auth
-