fix(auth): логировать тело ответа при ошибке OAuth-обмена кода
Живая проверка Google-входа упала с "401 Unauthorized" на /token, но лог показывал только код статуса — тело ответа (там у Google/Яндекс error/ error_description с точной причиной: invalid_client и т.п.) терялось. _raise_for_status_verbose() оборачивает raise_for_status(), добавляя resp.text в сообщение исключения — на все 4 вызова (token+userinfo × google+yandex). Чисто диагностическое изменение, поведение не меняет. Тесты/линт/mypy — ок. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,18 @@ class OAuthNotConfigured(Exception):
|
|||||||
"""Провайдер не настроен (пустой client_id/secret в конфиге)."""
|
"""Провайдер не настроен (пустой client_id/secret в конфиге)."""
|
||||||
|
|
||||||
|
|
||||||
|
def _raise_for_status_verbose(resp: httpx.Response) -> None:
|
||||||
|
"""Как raise_for_status(), но с телом ответа в сообщении — у Google/Яндекс
|
||||||
|
там error/error_description с точной причиной (invalid_client и т.п.),
|
||||||
|
без этого в логе только код статуса, причина не видна."""
|
||||||
|
try:
|
||||||
|
resp.raise_for_status()
|
||||||
|
except httpx.HTTPStatusError as e:
|
||||||
|
raise httpx.HTTPStatusError(
|
||||||
|
f"{e}: {resp.text[:500]}", request=e.request, response=e.response
|
||||||
|
) from e
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class OAuthUserInfo:
|
class OAuthUserInfo:
|
||||||
provider_id: str
|
provider_id: str
|
||||||
@@ -86,14 +98,14 @@ async def exchange_code(provider: str, code: str) -> OAuthUserInfo:
|
|||||||
"grant_type": "authorization_code",
|
"grant_type": "authorization_code",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
token_resp.raise_for_status()
|
_raise_for_status_verbose(token_resp)
|
||||||
access_token = token_resp.json()["access_token"]
|
access_token = token_resp.json()["access_token"]
|
||||||
|
|
||||||
info_resp = await client.get(
|
info_resp = await client.get(
|
||||||
"https://www.googleapis.com/oauth2/v3/userinfo",
|
"https://www.googleapis.com/oauth2/v3/userinfo",
|
||||||
headers={"Authorization": f"Bearer {access_token}"},
|
headers={"Authorization": f"Bearer {access_token}"},
|
||||||
)
|
)
|
||||||
info_resp.raise_for_status()
|
_raise_for_status_verbose(info_resp)
|
||||||
info = info_resp.json()
|
info = info_resp.json()
|
||||||
email = info["email"]
|
email = info["email"]
|
||||||
return OAuthUserInfo(
|
return OAuthUserInfo(
|
||||||
@@ -113,7 +125,7 @@ async def exchange_code(provider: str, code: str) -> OAuthUserInfo:
|
|||||||
"client_secret": client_secret,
|
"client_secret": client_secret,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
token_resp.raise_for_status()
|
_raise_for_status_verbose(token_resp)
|
||||||
access_token = token_resp.json()["access_token"]
|
access_token = token_resp.json()["access_token"]
|
||||||
|
|
||||||
info_resp = await client.get(
|
info_resp = await client.get(
|
||||||
@@ -121,7 +133,7 @@ async def exchange_code(provider: str, code: str) -> OAuthUserInfo:
|
|||||||
params={"format": "json"},
|
params={"format": "json"},
|
||||||
headers={"Authorization": f"OAuth {access_token}"},
|
headers={"Authorization": f"OAuth {access_token}"},
|
||||||
)
|
)
|
||||||
info_resp.raise_for_status()
|
_raise_for_status_verbose(info_resp)
|
||||||
info = info_resp.json()
|
info = info_resp.json()
|
||||||
|
|
||||||
email = info.get("default_email") or next(iter(info.get("emails") or []), None)
|
email = info.get("default_email") or next(iter(info.get("emails") or []), None)
|
||||||
|
|||||||
Reference in New Issue
Block a user