feat(desktop): 支持桌面端三十天免登录

This commit is contained in:
Cheng Zhou
2026-07-02 09:06:24 +08:00
parent 46f488c8ae
commit f33cc0948e
9 changed files with 334 additions and 19 deletions
+69 -1
View File
@@ -16,7 +16,7 @@ import os
from app.main import create_app
from app.core.config import settings
from app.core.deps import get_db_session
from app.core.security import hash_password, verify_password
from app.core.security import create_access_token, decode_token_allow_expired, hash_password, verify_password
from app.crud import user as user_crud
from app.db.base_class import Base
from app.models.audit_log import AuditLog
@@ -422,6 +422,74 @@ async def test_registered_user_can_login_after_email_verification(client_and_db)
assert resp.json()["access_token"]
@pytest.mark.asyncio
async def test_desktop_login_uses_30_day_token_without_changing_web_login(client_and_db):
client, _ = client_and_db
web_resp = await encrypted_login(client, "admin@test.com", "admin123")
desktop_resp = await client.post(
"/api/v1/auth/login",
json=await encrypted_auth_payload(client, "admin@test.com", "admin123"),
headers={"X-CTMS-Client-Type": "desktop"},
)
assert web_resp.status_code == 200
assert desktop_resp.status_code == 200
web_payload = decode_token_allow_expired(web_resp.json()["access_token"])
desktop_payload = decode_token_allow_expired(desktop_resp.json()["access_token"])
assert web_payload["client_type"] == "web"
assert desktop_payload["client_type"] == "desktop"
assert web_payload["exp"] - web_payload["iat"] == settings.JWT_EXPIRE_MINUTES * 60
assert desktop_payload["exp"] - desktop_payload["iat"] == settings.DESKTOP_SESSION_MAX_DAYS * 24 * 3600
@pytest.mark.asyncio
async def test_web_token_extension_cannot_be_upgraded_with_desktop_header(client_and_db):
client, _ = client_and_db
web_resp = await encrypted_login(client, "admin@test.com", "admin123")
token = web_resp.json()["access_token"]
resp = await client.post(
"/api/v1/auth/extend",
headers={
"Authorization": f"Bearer {token}",
"X-CTMS-Client-Type": "desktop",
},
)
assert resp.status_code == 200
payload = decode_token_allow_expired(resp.json()["accessToken"])
assert payload["client_type"] == "web"
assert payload["exp"] - payload["iat"] == settings.JWT_EXPIRE_MINUTES * 60
@pytest.mark.asyncio
async def test_desktop_token_extension_rejects_sessions_after_30_days(client_and_db):
client, SessionLocal = client_and_db
async with SessionLocal() as session:
admin = await user_crud.get_by_email(session, "admin@test.com")
session_start = datetime.now(timezone.utc) - timedelta(days=settings.DESKTOP_SESSION_MAX_DAYS, seconds=1)
token = create_access_token(
user_id=str(admin.id),
expires_minutes=settings.DESKTOP_SESSION_MAX_DAYS * 24 * 60,
session_start=session_start,
max_age_seconds=settings.DESKTOP_SESSION_MAX_DAYS * 24 * 3600,
client_type="desktop",
)
resp = await client.post(
"/api/v1/auth/extend",
headers={
"Authorization": f"Bearer {token}",
"X-CTMS-Client-Type": "desktop",
},
)
assert resp.status_code == 401
assert "会话已到期" in resp.json().get("detail", "")
@pytest.mark.asyncio
async def test_admin_created_user_is_active_by_default(client_and_db):
client, SessionLocal = client_and_db