Skip to main content

jellytau_lib/auth/
session_verifier.rs

1use serde::Serialize;
2use std::sync::atomic::{AtomicBool, Ordering};
3use std::sync::Arc;
4use std::time::Duration;
5use tauri::{AppHandle, Emitter};
6
7use super::{AuthManager, User};
8
9// Verification interval (5 minutes)
10const VERIFICATION_INTERVAL_MS: u64 = 300000;
11
12/// Session verification result event emitted to frontend
13#[derive(Debug, Clone, Serialize)]
14#[serde(rename_all = "camelCase", tag = "type")]
15pub enum SessionVerificationEvent {
16    Verified { user: User },
17    NeedsReauth { reason: String },
18    NetworkError { message: String },
19}
20
21/// Background session verifier
22pub struct SessionVerifier {
23    auth_manager: Arc<AuthManager>,
24    is_running: Arc<AtomicBool>,
25    device_id: String,
26    app_handle: Option<AppHandle>,
27}
28
29impl SessionVerifier {
30    /// Create a new session verifier
31    pub fn new(auth_manager: Arc<AuthManager>, device_id: String) -> Self {
32        Self {
33            auth_manager,
34            is_running: Arc::new(AtomicBool::new(false)),
35            device_id,
36            app_handle: None,
37        }
38    }
39
40    /// Set the Tauri app handle for event emission
41    pub fn set_app_handle(&mut self, app_handle: AppHandle) {
42        self.app_handle = Some(app_handle);
43    }
44
45    /// Start periodic session verification
46    pub async fn start(&self) {
47        if self.is_running.swap(true, Ordering::SeqCst) {
48            log::info!("[SessionVerifier] Already running");
49            return;
50        }
51
52        log::info!("[SessionVerifier] Starting background verification");
53
54        let auth_manager = Arc::clone(&self.auth_manager);
55        let is_running = Arc::clone(&self.is_running);
56        let device_id = self.device_id.clone();
57        let app_handle = self.app_handle.clone();
58
59        tokio::spawn(async move {
60            // Initial verification after short delay
61            tokio::time::sleep(Duration::from_millis(2000)).await;
62
63            while is_running.load(Ordering::SeqCst) {
64                // Get current session
65                let session = auth_manager.get_session().await;
66
67                if let Some(session) = session {
68                    log::debug!(
69                        "[SessionVerifier] Verifying session for: {}",
70                        session.username
71                    );
72
73                    // Verify the session
74                    match auth_manager
75                        .verify_session(
76                            &session.server_url,
77                            &session.user_id,
78                            &session.access_token,
79                            &device_id,
80                        )
81                        .await
82                    {
83                        Ok(user) => {
84                            log::info!("[SessionVerifier] Session verified successfully");
85
86                            // Emit success event
87                            if let Some(app) = &app_handle {
88                                let event = SessionVerificationEvent::Verified { user };
89                                if let Err(e) = app.emit("auth:session-verified", event) {
90                                    log::error!("[SessionVerifier] Failed to emit event: {}", e);
91                                }
92                            }
93
94                            // Update session as verified
95                            let mut updated_session = session;
96                            updated_session.verified = true;
97                            updated_session.needs_reauth = false;
98                            auth_manager.set_session(Some(updated_session)).await;
99                        }
100                        Err(e) => {
101                            log::warn!("[SessionVerifier] Verification failed: {}", e);
102
103                            // Classify error
104                            let is_auth_error = e.contains("401") || e.contains("403");
105                            let is_network_error = e.contains("network")
106                                || e.contains("timeout")
107                                || e.contains("connection")
108                                || e.contains("DNS");
109
110                            if is_auth_error {
111                                // Token is invalid - need re-authentication
112                                log::warn!("[SessionVerifier] Session requires re-authentication");
113
114                                if let Some(app) = &app_handle {
115                                    let event = SessionVerificationEvent::NeedsReauth {
116                                        reason: "Session expired".to_string(),
117                                    };
118                                    if let Err(e) = app.emit("auth:needs-reauth", event) {
119                                        log::error!(
120                                            "[SessionVerifier] Failed to emit event: {}",
121                                            e
122                                        );
123                                    }
124                                }
125
126                                // Update session
127                                let mut updated_session = session;
128                                updated_session.verified = false;
129                                updated_session.needs_reauth = true;
130                                auth_manager.set_session(Some(updated_session)).await;
131                            } else if is_network_error {
132                                // Network error - keep using cached session
133                                log::info!("[SessionVerifier] Network error during verification, keeping cached session");
134
135                                if let Some(app) = &app_handle {
136                                    let event = SessionVerificationEvent::NetworkError {
137                                        message: e.clone(),
138                                    };
139                                    if let Err(e) = app.emit("auth:network-error", event) {
140                                        log::error!(
141                                            "[SessionVerifier] Failed to emit event: {}",
142                                            e
143                                        );
144                                    }
145                                }
146                            } else {
147                                // Unknown error - log but don't invalidate
148                                log::error!(
149                                    "[SessionVerifier] Unknown error during verification: {}",
150                                    e
151                                );
152                            }
153                        }
154                    }
155                }
156
157                // Wait for next verification
158                tokio::time::sleep(Duration::from_millis(VERIFICATION_INTERVAL_MS)).await;
159            }
160
161            log::info!("[SessionVerifier] Stopped");
162        });
163    }
164
165    /// Stop periodic verification
166    pub fn stop(&self) {
167        log::info!("[SessionVerifier] Stopping background verification");
168        self.is_running.store(false, Ordering::SeqCst);
169    }
170}