diff --git a/app/api/auth.py b/app/api/auth.py index d631383..561f40d 100644 --- a/app/api/auth.py +++ b/app/api/auth.py @@ -9,6 +9,37 @@ from app.schemas.login import LoginRequest, LoginResponse router = APIRouter(prefix="/auth", tags=["auth"]) +def _resolve_username_by_email(settings, email: str) -> str | None: + if not settings.authentik_base_url or not settings.authentik_admin_token: + return None + + url = urljoin(settings.authentik_base_url.rstrip("/") + "/", "api/v3/core/users/") + try: + resp = httpx.get( + url, + params={"email": email}, + timeout=10, + verify=settings.authentik_verify_tls, + headers={ + "Authorization": f"Bearer {settings.authentik_admin_token}", + "Accept": "application/json", + }, + ) + except Exception: + return None + + if resp.status_code >= 400: + return None + + data = resp.json() + results = data.get("results") if isinstance(data, dict) else None + if not isinstance(results, list) or not results: + return None + + username = results[0].get("username") + return username if isinstance(username, str) and username else None + + @router.post("/login", response_model=LoginResponse) def login(payload: LoginRequest) -> LoginResponse: settings = get_settings() @@ -30,17 +61,31 @@ def login(payload: LoginRequest) -> LoginResponse: "scope": "openid profile email", } - try: + def _token_request(form_data: dict[str, str]) -> httpx.Response: resp = httpx.post( token_endpoint, - data=form, + data=form_data, timeout=10, verify=settings.authentik_verify_tls, headers={"Content-Type": "application/x-www-form-urlencoded"}, ) + return resp + + try: + resp = _token_request(form) except Exception as exc: raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail="authentik_unreachable") from exc + # If user entered email, try resolving username and retry once. + if resp.status_code >= 400 and "@" in payload.username: + resolved = _resolve_username_by_email(settings, payload.username) + if resolved and resolved != payload.username: + form["username"] = resolved + try: + resp = _token_request(form) + except Exception as exc: + raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail="authentik_unreachable") from exc + if resp.status_code >= 400: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid_username_or_password")