Enhance Telegram bot functionality and improve error handling
- Introduced a new function to set the default menu button for the Telegram bot's Web App. - Updated the initData validation process to provide detailed error messages for authorization failures. - Refactored the validate_init_data function to return both username and reason for validation failure. - Enhanced the web application to handle access denial more gracefully, providing users with hints on how to access the calendar. - Improved the README with additional instructions for configuring the bot's menu button and Web App URL. - Updated tests to reflect changes in the validation process and error handling.
This commit is contained in:
22
api/app.py
22
api/app.py
@@ -12,7 +12,7 @@ from fastapi.staticfiles import StaticFiles
|
||||
from db.session import session_scope
|
||||
from db.repository import get_duties
|
||||
from db.schemas import DutyWithUser
|
||||
from api.telegram_auth import validate_init_data
|
||||
from api.telegram_auth import validate_init_data_with_reason
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -50,6 +50,16 @@ def _fetch_duties_response(from_date: str, to_date: str) -> list[DutyWithUser]:
|
||||
]
|
||||
|
||||
|
||||
def _auth_error_detail(auth_reason: str) -> str:
|
||||
"""Return user-facing detail message for 403 when initData validation fails."""
|
||||
if auth_reason == "hash_mismatch":
|
||||
return (
|
||||
"Неверная подпись. Убедитесь, что BOT_TOKEN на сервере совпадает с токеном бота, "
|
||||
"из которого открыт календарь (тот же бот, что в меню)."
|
||||
)
|
||||
return "Неверные данные авторизации"
|
||||
|
||||
|
||||
def _is_private_client(client_host: str | None) -> bool:
|
||||
"""True if client is localhost or private LAN (dev / same-machine access).
|
||||
|
||||
@@ -110,12 +120,12 @@ def list_duties(
|
||||
log.warning("duties: no X-Telegram-Init-Data header (client=%s)", client_host)
|
||||
raise HTTPException(status_code=403, detail="Откройте календарь из Telegram")
|
||||
max_age = config.INIT_DATA_MAX_AGE_SECONDS or None
|
||||
username = validate_init_data(init_data, config.BOT_TOKEN, max_age_seconds=max_age)
|
||||
username, auth_reason = validate_init_data_with_reason(
|
||||
init_data, config.BOT_TOKEN, max_age_seconds=max_age
|
||||
)
|
||||
if username is None:
|
||||
log.warning(
|
||||
"duties: initData validation failed (invalid signature or no username)"
|
||||
)
|
||||
raise HTTPException(status_code=403, detail="Неверные данные авторизации")
|
||||
log.warning("duties: initData validation failed: %s", auth_reason)
|
||||
raise HTTPException(status_code=403, detail=_auth_error_detail(auth_reason))
|
||||
if not config.can_access_miniapp(username):
|
||||
log.warning("duties: username not in allowlist")
|
||||
raise HTTPException(status_code=403, detail="Доступ запрещён")
|
||||
|
||||
Reference in New Issue
Block a user