Добавить проверку токена прокси, динамические лимиты трафика и Telegram magic-link вход
Прокси теперь может валидировать клиентские JWT и отчитываться о расходе трафика через новые internal-эндпоинты (защищены X-Internal-Secret), лимит на юзера настраивается динамически через admin PATCH без передеплоя прокси. Заодно реализован задокументированный, но не подключённый флоу входа через Telegram-бота (magic-link) для десктоп/мобильного приложения, и seed-эндпоинты теперь возвращают access_token в JSON (не только в cookie) — без этого Tauri-приложение не могло ими пользоваться. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -239,6 +239,76 @@ impl AuthService {
|
||||
self.issue_session(user.id, user.tg_id, jwt_secret, user_agent)
|
||||
.await
|
||||
}
|
||||
// --- Magic-link вход через Telegram-бота для десктоп/мобильного приложения ---
|
||||
// Приложение не может встроить Telegram Login Widget (нет домена/WebView-моста
|
||||
// как у веб-ЛК), поэтому вместо него: приложение создаёт auth_code и открывает
|
||||
// `t.me/<bot>?start=<auth_code>` (см. `bot.rs::handle_commands`), а само поллит
|
||||
// `GET /auth/telegram/session/{code}`, пока бот не подтвердит код.
|
||||
|
||||
/// Создаёт ожидающую подтверждения сессию, возвращает код для deep-link.
|
||||
pub async fn create_telegram_login_code(&self) -> Result<String, AppError> {
|
||||
let auth_code: String = rand::thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(8)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
self.repo
|
||||
.create_auth_session(&auth_code)
|
||||
.await
|
||||
.map_err(AppError::Database)?;
|
||||
Ok(auth_code)
|
||||
}
|
||||
|
||||
/// Вызывается ботом на `/start <auth_code>`: если код существует, ещё не
|
||||
/// подтверждён и не истёк — привязывает его к вошедшему в боте юзеру.
|
||||
pub async fn confirm_telegram_login_code(
|
||||
&self,
|
||||
auth_code: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<bool, AppError> {
|
||||
self.repo
|
||||
.verify_auth_session(auth_code, user_id)
|
||||
.await
|
||||
.map_err(AppError::Database)
|
||||
}
|
||||
|
||||
/// Опрашивается приложением. `Ok(None)` — код существует, ждём подтверждения
|
||||
/// в боте; `Err(Unauthorized)` — код не найден или истёк.
|
||||
#[instrument(skip(self, jwt_secret))]
|
||||
pub async fn poll_telegram_login(
|
||||
&self,
|
||||
auth_code: &str,
|
||||
jwt_secret: &str,
|
||||
user_agent: Option<&str>,
|
||||
) -> Result<Option<Session>, AppError> {
|
||||
let (user_id, is_verified, expires_at) = self
|
||||
.repo
|
||||
.get_auth_session(auth_code)
|
||||
.await
|
||||
.map_err(AppError::Database)?
|
||||
.ok_or_else(|| AppError::Unauthorized("Invalid auth code".into()))?;
|
||||
|
||||
if expires_at < Utc::now() {
|
||||
return Err(AppError::Unauthorized("Auth code expired".into()));
|
||||
}
|
||||
if !is_verified {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let user_id = user_id
|
||||
.ok_or_else(|| AppError::Internal("Verified auth session missing user_id".into()))?;
|
||||
let user = self
|
||||
.repo
|
||||
.get_user_by_id(user_id)
|
||||
.await
|
||||
.map_err(AppError::Database)?
|
||||
.ok_or(AppError::UserNotFound)?;
|
||||
|
||||
self.issue_session(user.id, user.tg_id, jwt_secret, user_agent)
|
||||
.await
|
||||
.map(Some)
|
||||
}
|
||||
|
||||
// --- Логика Telegram (без изменений, кроме вызова генерации токена) ---
|
||||
|
||||
// Сигнатура повторяет поля payload-а Telegram Login Widget — это намеренно.
|
||||
|
||||
Reference in New Issue
Block a user