fix: clarify New-API authentication failures

This commit is contained in:
2026-07-22 00:49:32 +08:00
parent 6906c3b998
commit db51733383
4 changed files with 91 additions and 2 deletions
+20
View File
@@ -176,6 +176,12 @@ def _clean_auth_header_value(value: Any, field_name: str) -> str:
return text
def _is_dashboard_jwt(value: Any) -> bool:
"""识别 New-API 面板登录 JWT,避免将其当作长期 API Token 使用。"""
token = str(value or "").strip()
return len(token) > 512 and token.count(".") == 2
def _find_user_id(value: Any) -> str:
if isinstance(value, dict):
for key in ("id", "user_id", "userId"):
@@ -537,6 +543,11 @@ class UpstreamClient:
headers["Nox-Api-User"] = user_id
elif self.auth_type == "new_api_token":
token = _clean_auth_header_value(self.auth_config.get("token", ""), "New-API access token")
if _is_dashboard_jwt(token):
raise UpstreamError(
"New-API token 是面板登录 JWT,不是用户 API Token"
"请在上游 Token 页面重新生成 Access Token"
)
user_id = self._user_header_value()
if token:
headers["Authorization"] = f"Bearer {token}"
@@ -1095,6 +1106,15 @@ class UpstreamClient:
if not email or not password:
raise UpstreamError("login_password auth requires email and password in auth_config")
resp = self._request("POST", login_path, {username_field: email, "password": password}, auth=False)
data = _unwrap_data(resp)
if isinstance(data, dict) and data.get("require_2fa"):
raise UpstreamError(
"New-API 账号启用了二次验证,SmartUp 暂不支持密码登录 2FA"
"请改用 Access Token 或 Cookie"
)
if not _is_success_response(resp):
message = _response_message(resp, "New-API 登录失败")
raise UpstreamError(message)
token = _find_token(resp)
if token:
self._token = token
+15 -1
View File
@@ -1,4 +1,6 @@
from app.services.upstream_client import UpstreamClient
import pytest
from app.services.upstream_client import UpstreamClient, UpstreamError
def test_new_api_token_headers_include_authorization_and_new_api_user():
@@ -16,6 +18,18 @@ def test_new_api_token_headers_include_authorization_and_new_api_user():
assert "Nox-Api-User" not in headers
def test_new_api_token_rejects_dashboard_jwt():
client = UpstreamClient(
base_url="http://newapi.local",
api_prefix="",
auth_type="new_api_token",
auth_config={"token": "a" * 1200 + ".payload.signature", "user_id": "7"},
)
with pytest.raises(UpstreamError, match="面板登录 JWT"):
client._headers()
def test_nox_token_headers_include_authorization_and_nox_api_user():
client = UpstreamClient(
base_url="http://nox.local",
+39 -1
View File
@@ -8,7 +8,7 @@ import httpx
import pytest
from app.services import upstream_client
from app.services.upstream_client import UpstreamClient
from app.services.upstream_client import UpstreamClient, UpstreamError
class FakeHttpClient:
@@ -412,3 +412,41 @@ def test_login_password_empty_prefix_defaults_to_user_api_login(monkeypatch):
assert headers["Authorization"] == "Bearer nox-access"
assert headers["Nox-Api-User"] == "9"
assert headers["New-Api-User"] == "9"
def test_login_password_surfaces_business_error_response(monkeypatch):
def handler(request: httpx.Request, kwargs):
return _response(request, 200, {
"success": False,
"message": "用户名或密码错误",
})
_install_fake_client(monkeypatch, handler)
client = UpstreamClient(
base_url="http://newapi.local",
api_prefix="",
auth_type="login_password",
auth_config={"email": "admin", "password": "wrong"},
)
with pytest.raises(UpstreamError, match="用户名或密码错误"):
client.login()
def test_login_password_reports_unsupported_two_factor(monkeypatch):
def handler(request: httpx.Request, kwargs):
return _response(request, 200, {
"success": True,
"data": {"require_2fa": True, "flow_token": "flow"},
})
_install_fake_client(monkeypatch, handler)
client = UpstreamClient(
base_url="http://newapi.local",
api_prefix="",
auth_type="login_password",
auth_config={"email": "admin", "password": "secret"},
)
with pytest.raises(UpstreamError, match="二次验证"):
client.login()