From 8f4a58d33d4c5f14ffb1ad586c6009260f642430 Mon Sep 17 00:00:00 2001 From: NotBigGhost Date: Fri, 26 Jun 2026 15:33:27 +0300 Subject: [PATCH] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=20=D0=B1=D0=B0=D0=B3?= =?UTF-8?q?=D0=B0=20=D1=81=20=D0=B4=D1=83=D0=B1=D0=BB=D0=B5=D0=BC=20=D0=B0?= =?UTF-8?q?=D0=BA=D0=BA=D0=B0=D1=83=D0=BD=D1=82=D0=BE=D0=B2=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BD=D0=B5=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D0=BD=D0=B8=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/routers/dev_auth.py | 16 +++++++++++-- backend/tests/test_auth.py | 41 +++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/backend/app/routers/dev_auth.py b/backend/app/routers/dev_auth.py index 0a32fcb..7f36f2f 100644 --- a/backend/app/routers/dev_auth.py +++ b/backend/app/routers/dev_auth.py @@ -10,7 +10,7 @@ from fastapi import APIRouter, Depends, Request, Response from sqlmodel import Session, select from app.auth.dev_stub import DevStubProvider -from app.auth.login import login_with_identity +from app.auth.login import establish_session, login_with_identity from app.auth.provider import ExternalIdentity from app.db.session import get_session from app.models import User @@ -28,7 +28,19 @@ def dev_login( response: Response, session: Session = Depends(get_session), ) -> s.MeRead: - identity = DevStubProvider().authenticate({"nickname": body.nickname}) + # Dev-вход «стать игроком с этим ником». Если такой игрок уже есть (любого провайдера — + # Telegram, или stub с изменённым позже ником), входим именно в него, а не плодим дубли. + # Только если игрока с таким ником нет — создаём нового stub-пользователя. + nickname = (body.nickname or "").strip() + existing = ( + session.exec(select(User).where(User.nickname == nickname)).first() + if nickname + else None + ) + if existing is not None: + user = establish_session(session, response, request, existing, existing.auth_provider) + return build_me(session, user) + identity = DevStubProvider().authenticate({"nickname": nickname}) user = login_with_identity(session, response, request, identity) return build_me(session, user) diff --git a/backend/tests/test_auth.py b/backend/tests/test_auth.py index 810ad4f..184be38 100644 --- a/backend/tests/test_auth.py +++ b/backend/tests/test_auth.py @@ -58,6 +58,47 @@ def _telegram_payload(token: str, **fields) -> dict: return {**data, "hash": sig} +def test_dev_login_into_telegram_user_no_duplicate(client: TestClient, engine, monkeypatch): + """Dev-вход по нику Telegram-аккаунта входит в него, а не создаёт дубль «ник 2».""" + from sqlmodel import Session, select + + from app.core.config import settings + from app.models import User + + monkeypatch.setattr(settings, "telegram_bot_token", "TEST_BOT_TOKEN") + r = client.post("/api/auth/telegram", json=_telegram_payload("TEST_BOT_TOKEN")) + assert r.status_code == 200, r.text + tg_id, nick = r.json()["id"], r.json()["nickname"] + + client.cookies.clear() # «другой вход» — без сессии + r2 = client.post("/api/auth/dev/login", json={"nickname": nick}) + assert r2.status_code == 200, r2.text + assert r2.json()["id"] == tg_id # тот же аккаунт, не новый + + with Session(engine) as s: + users = s.exec(select(User).where(User.nickname == nick)).all() + assert len(users) == 1 # дубликат не создан + + +def test_dev_login_after_rename_no_duplicate(client: TestClient, engine): + """После смены ника dev-вход по новому нику входит в того же игрока (без дубля).""" + from sqlmodel import Session, select + + from app.models import User + + uid = client.post("/api/auth/dev/login", json={"nickname": "Старый"}).json()["id"] + pr = client.patch("/api/users/me", json={"nickname": "Новый"}, headers=csrf_headers(client)) + assert pr.status_code == 200, pr.text + + client.cookies.clear() + r2 = client.post("/api/auth/dev/login", json={"nickname": "Новый"}) + assert r2.status_code == 200, r2.text + assert r2.json()["id"] == uid + + with Session(engine) as s: + assert len(s.exec(select(User).where(User.nickname == "Новый")).all()) == 1 + + def test_telegram_login_valid_signature(client: TestClient, monkeypatch): from app.core.config import settings