jellytau/src-tauri/src/auth/session_verifier.rs

159 lines
6.5 KiB
Rust

use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use tauri::{AppHandle, Emitter};
use serde::Serialize;
use super::{AuthManager, User};
// Verification interval (5 minutes)
const VERIFICATION_INTERVAL_MS: u64 = 300000;
/// Session verification result event emitted to frontend
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase", tag = "type")]
pub enum SessionVerificationEvent {
Verified { user: User },
NeedsReauth { reason: String },
NetworkError { message: String },
}
/// Background session verifier
pub struct SessionVerifier {
auth_manager: Arc<AuthManager>,
is_running: Arc<AtomicBool>,
device_id: String,
app_handle: Option<AppHandle>,
}
impl SessionVerifier {
/// Create a new session verifier
pub fn new(auth_manager: Arc<AuthManager>, device_id: String) -> Self {
Self {
auth_manager,
is_running: Arc::new(AtomicBool::new(false)),
device_id,
app_handle: None,
}
}
/// Set the Tauri app handle for event emission
pub fn set_app_handle(&mut self, app_handle: AppHandle) {
self.app_handle = Some(app_handle);
}
/// Start periodic session verification
pub async fn start(&self) {
if self.is_running.swap(true, Ordering::SeqCst) {
log::info!("[SessionVerifier] Already running");
return;
}
log::info!("[SessionVerifier] Starting background verification");
let auth_manager = Arc::clone(&self.auth_manager);
let is_running = Arc::clone(&self.is_running);
let device_id = self.device_id.clone();
let app_handle = self.app_handle.clone();
tokio::spawn(async move {
// Initial verification after short delay
tokio::time::sleep(Duration::from_millis(2000)).await;
while is_running.load(Ordering::SeqCst) {
// Get current session
let session = auth_manager.get_session().await;
if let Some(session) = session {
log::debug!("[SessionVerifier] Verifying session for: {}", session.username);
// Verify the session
match auth_manager
.verify_session(
&session.server_url,
&session.user_id,
&session.access_token,
&device_id,
)
.await
{
Ok(user) => {
log::info!("[SessionVerifier] Session verified successfully");
// Emit success event
if let Some(app) = &app_handle {
let event = SessionVerificationEvent::Verified { user };
if let Err(e) = app.emit("auth:session-verified", event) {
log::error!("[SessionVerifier] Failed to emit event: {}", e);
}
}
// Update session as verified
let mut updated_session = session;
updated_session.verified = true;
updated_session.needs_reauth = false;
auth_manager.set_session(Some(updated_session)).await;
}
Err(e) => {
log::warn!("[SessionVerifier] Verification failed: {}", e);
// Classify error
let is_auth_error = e.contains("401") || e.contains("403");
let is_network_error = e.contains("network")
|| e.contains("timeout")
|| e.contains("connection")
|| e.contains("DNS");
if is_auth_error {
// Token is invalid - need re-authentication
log::warn!("[SessionVerifier] Session requires re-authentication");
if let Some(app) = &app_handle {
let event = SessionVerificationEvent::NeedsReauth {
reason: "Session expired".to_string(),
};
if let Err(e) = app.emit("auth:needs-reauth", event) {
log::error!("[SessionVerifier] Failed to emit event: {}", e);
}
}
// Update session
let mut updated_session = session;
updated_session.verified = false;
updated_session.needs_reauth = true;
auth_manager.set_session(Some(updated_session)).await;
} else if is_network_error {
// Network error - keep using cached session
log::info!("[SessionVerifier] Network error during verification, keeping cached session");
if let Some(app) = &app_handle {
let event = SessionVerificationEvent::NetworkError {
message: e.clone(),
};
if let Err(e) = app.emit("auth:network-error", event) {
log::error!("[SessionVerifier] Failed to emit event: {}", e);
}
}
} else {
// Unknown error - log but don't invalidate
log::error!("[SessionVerifier] Unknown error during verification: {}", e);
}
}
}
}
// Wait for next verification
tokio::time::sleep(Duration::from_millis(VERIFICATION_INTERVAL_MS)).await;
}
log::info!("[SessionVerifier] Stopped");
});
}
/// Stop periodic verification
pub fn stop(&self) {
log::info!("[SessionVerifier] Stopping background verification");
self.is_running.store(false, Ordering::SeqCst);
}
}