49 lines
1.9 KiB
Python
49 lines
1.9 KiB
Python
"""OAuth2 login/callback for Saxo — paths outside /api, matching the callback URL
|
|
registered on the Saxo app dashboard (https://openfin.open-squared.tech/oauth/saxo/callback)."""
|
|
import logging
|
|
|
|
from fastapi import APIRouter, HTTPException, Query
|
|
from fastapi.responses import RedirectResponse
|
|
|
|
from services import saxo_auth
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter(tags=["saxo-oauth"])
|
|
|
|
FRONTEND_CONFIG_URL = "/config"
|
|
|
|
|
|
@router.get("/oauth/saxo/login")
|
|
def saxo_login():
|
|
if not saxo_auth.is_configured():
|
|
raise HTTPException(status_code=400, detail="SAXO_APP_KEY / SAXO_APP_SECRET manquants dans backend/.env")
|
|
state = saxo_auth.new_login_state()
|
|
return RedirectResponse(saxo_auth.build_authorize_url(state))
|
|
|
|
|
|
@router.get("/oauth/saxo/callback")
|
|
def saxo_callback(code: str = Query(...), state: str = Query(...)):
|
|
if not saxo_auth.consume_and_verify_state(state):
|
|
raise HTTPException(status_code=400, detail="État OAuth invalide (CSRF) — relancez la connexion depuis Config.")
|
|
try:
|
|
saxo_auth.exchange_code_for_tokens(code)
|
|
except Exception as e:
|
|
logger.error(f"[Saxo OAuth] Token exchange failed: {e}")
|
|
return RedirectResponse(f"{FRONTEND_CONFIG_URL}?saxo_error=1")
|
|
|
|
# Snapshot the whole watchlist right away — don't make the user wait for the next
|
|
# scheduled poll after connecting. Runs in the background so the redirect isn't delayed.
|
|
import threading
|
|
from services.saxo_scheduler import run_snapshot_pass
|
|
threading.Thread(target=run_snapshot_pass, daemon=True, name="saxo-connect-snapshot").start()
|
|
|
|
return RedirectResponse(f"{FRONTEND_CONFIG_URL}?saxo_connected=1")
|
|
|
|
|
|
@router.post("/oauth/saxo/refresh")
|
|
def saxo_manual_refresh():
|
|
result = saxo_auth.refresh_tokens()
|
|
if not result:
|
|
raise HTTPException(status_code=400, detail="Saxo n'est pas connecté")
|
|
return {"refreshed": True}
|