Преминете към основното съдържание

Автентикация

Модулът auth/ управлява целия lifecycle на автентикацията: OAuth вход чрез browser redirect, JWT съхранение и валидация, периодично обновяване на токени, и Stripe checkout интеграция.

State Machine

AuthState enum

pub enum AuthState {
NotLoggedIn,
WaitingForBrowser { started: Instant, port: u16 },
LoggedIn(AccountInfo),
OfflineValid { account: AccountInfo, days_remaining: u32 },
Expired,
DeviceLimitBlocked { retry_local_time: String },
}

Browser Auth Flow (Login)

Race условия

Потокът използва tokio::select! за race между три futures:

tokio::select! {
Some(params) = params_rx.recv() => { /* callback получен */ },
_ = tokio::time::sleep(Duration::from_secs(60)) => { /* timeout */ },
_ = cancel_rx => { /* потребителят отмени */ },
}

Checkout Flow (Stripe)

Подобен на login flow-а, но:

  1. Изисква съществуващ JWT (Bearer auth за /billing/create-checkout)
  2. Отваря страница за избор на план (/billing/choose-plan)
  3. Потребителят минава през Stripe Checkout
  4. Timeout е 120 секунди (вместо 60)
  5. При успех получава нов JWT с plan: "pro"

JWT управление

Структура на Claims

pub struct Claims {
pub sub: String, // email
pub user_id: String,
pub plan: String, // "free" или "pro"
pub device_id: String, // machine ID
pub provider: String, // "google", "azure", "github"
pub iat: i64, // issued at (Unix epoch)
pub exp: i64, // expiry (Unix epoch)
}

Token Storage

Токените се съхраняват в системния credential manager чрез keyring crate:

ЗаписService NameUser
JWT access tokendictarojwt_token
Refresh tokendictarojwt_refresh

На Windows това е Windows Credential Manager. На macOS -- Apple Keychain.

Token Validation

Валидацията използва вграден RS256 public key:

const RS256_PUBLIC_KEY_PEM: &str = "-----BEGIN PUBLIC KEY-----...";

pub fn validate_token(token: &str) -> Result<Claims, TokenError> {
let decoding_key = DecodingKey::from_rsa_pem(...);
let mut validation = Validation::new(Algorithm::RS256);
validation.leeway = 60; // 60-секунден tolerance за clock skew
jsonwebtoken::decode::<Claims>(token, &decoding_key, &validation)
}

Валидацията е изцяло client-side -- не се прави мрежова заявка. Това позволява offline работа.

Периодично обновяване на токени

ПараметърСтойност
REFRESH_INTERVAL_SECS3600 (1 час)
REFRESH_BACKOFF_SECS300 (5 минути при неуспех)

Restore при стартиране

При стартиране AuthManager::new() автоматично:

  1. Чете JWT от keyring (token::get_token())
  2. Валидира го локално (token::validate_token())
  3. Ако е валиден: state = LoggedIn(AccountInfo)
  4. Ако е невалиден: изтрива го и state = NotLoggedIn
  5. Ако няма токен: state = NotLoggedIn

Device Limit

Licensing сървърът ограничава броя акаунти на устройство. При превишаване:

  1. Callback URL съдържа error=device_limit&retry_at=RFC3339&cooldown_seconds=N
  2. retry_at се конвертира от UTC към локално време
  3. state = DeviceLimitBlocked { retry_local_time: "15:23" }
  4. UI показва кога потребителят може да опита отново

Customer Portal

start_portal() е fire-and-forget операция:

  1. POST към /account/portal-url с Bearer JWT
  2. Получава Stripe Customer Portal URL
  3. Отваря го в browser-а
  4. Потребителят управлява абонамента си директно в Stripe Portal
  5. Няма callback -- AuthState не се променя