diff --git a/app/services/auth.py b/app/services/auth.py index e4c0f46..3f47aca 100644 --- a/app/services/auth.py +++ b/app/services/auth.py @@ -127,6 +127,10 @@ class AuthService: # Генерируем токены access_token = create_access_token({"sub": user["username"], "uuid": user["uuid"]}) client_token = str(uuid.uuid4()) + + await sessions_collection.delete_many({ + "user_uuid": user["uuid"] + }) # Сохраняем сессию session = Session( @@ -147,14 +151,19 @@ class AuthService: } async def validate(self, access_token: str, client_token: str): - print(f"Searching for access_toke and client_token: '{access_token}', '{client_token}") session = await sessions_collection.find_one({ "access_token": access_token, "client_token": client_token, }) - print("Session from DB:", session) - if not session or datetime.utcnow() > session["expires_at"]: + + if not session: return False + + if datetime.utcnow() > session["expires_at"]: + # можно сразу чистить + await sessions_collection.delete_one({"_id": session["_id"]}) + return False + return True async def is_admin(self, access_token: str, client_token: str) -> bool: @@ -187,16 +196,46 @@ class AuthService: } async def refresh(self, access_token: str, client_token: str): - if not await self.validate(access_token, client_token): + session = await sessions_collection.find_one({ + "access_token": access_token, + "client_token": client_token, + }) + + if not session: return None - # Обновляем токен - new_access_token = create_access_token({"sub": "user", "uuid": "user_uuid"}) + if datetime.utcnow() > session["expires_at"]: + return None + + user = await users_collection.find_one({"uuid": session["user_uuid"]}) + if not user: + return None + + new_access_token = create_access_token({ + "sub": user["username"], + "uuid": user["uuid"], + }) + + new_expires_at = datetime.utcnow() + timedelta(minutes=1440) + await sessions_collection.update_one( - {"access_token": access_token}, - {"$set": {"access_token": new_access_token}}, + {"_id": session["_id"]}, + { + "$set": { + "access_token": new_access_token, + "expires_at": new_expires_at, + } + }, ) - return {"accessToken": new_access_token, "clientToken": client_token} + + return { + "accessToken": new_access_token, + "clientToken": client_token, + "selectedProfile": { + "id": user["uuid"], + "name": user["username"], + }, + } async def get_minecraft_profile(self, uuid: str): # Преобразуем UUID без дефисов в формат с дефисами (если нужно) @@ -291,12 +330,20 @@ class AuthService: async def join_server(self, request_data: dict): access_token = request_data.get("accessToken") - selected_profile = request_data.get("selectedProfile") # UUID без дефисов + selected_profile = request_data.get("selectedProfile") server_id = request_data.get("serverId") if not all([access_token, selected_profile, server_id]): raise HTTPException(status_code=400, detail="Missing required parameters") + session = await sessions_collection.find_one({ + "access_token": access_token, + "client_token": request_data.get("clientToken"), + }) + + if not session or datetime.utcnow() > session["expires_at"]: + raise HTTPException(status_code=401, detail="Invalid or expired session") + decoded_token = decode_token(access_token) if not decoded_token: raise HTTPException(status_code=401, detail="Invalid access token") @@ -305,11 +352,9 @@ class AuthService: if token_uuid != selected_profile: raise HTTPException(status_code=403, detail="Token doesn't match selected profile") - # Сохраняем server_id в сессию await sessions_collection.update_one( - {"user_uuid": decoded_token["uuid"]}, # UUID с дефисами + {"_id": session["_id"]}, {"$set": {"server_id": server_id}}, - upsert=True ) return True @@ -321,8 +366,9 @@ class AuthService: # Ищем сессию с этим server_id session = await sessions_collection.find_one({ - "user_uuid": user["uuid"], # UUID с дефисами - "server_id": server_id + "user_uuid": user["uuid"], + "server_id": server_id, + "expires_at": {"$gt": datetime.utcnow()}, }) if not session: raise HTTPException(status_code=403, detail="Not joined this server") diff --git a/main.py b/main.py index e32f169..ac9904c 100644 --- a/main.py +++ b/main.py @@ -11,6 +11,7 @@ from app.services.promo import PromoService from app.webhooks import telegram from app.db.database import users_collection from app.api import marketplace_ws +from app.db.database import users_collection, sessions_collection ###################### БОТ ###################### @@ -23,6 +24,12 @@ WEBHOOK_SECRET = os.getenv("TELEGRAM_WEBHOOK_SECRET", "") async def lifespan(app: FastAPI): # ===== STARTUP ===== + # TTL для сессий (автоочистка) + await sessions_collection.create_index( + "expires_at", + expireAfterSeconds=0 + ) + await users_collection.create_index("expires_at", expireAfterSeconds=0) await users_collection.create_index("telegram_user_id", unique=True, sparse=True)