native auth

This commit is contained in:
2026-02-11 01:10:08 +08:00
parent 624ee9a222
commit 8f91b6b680
14 changed files with 506 additions and 845 deletions

View File

@@ -12,17 +12,8 @@ use tracing::{error, info, warn};
static REFRESH_LOCK: once_cell::sync::Lazy<Mutex<()>> =
once_cell::sync::Lazy::new(|| Mutex::new(()));
#[derive(Default, Clone)]
pub struct OAuthFlowTracker {
pub state: Option<String>,
pub code_verifier: Option<String>,
pub initiated_at: Option<u64>,
pub cancel_token: Option<tokio_util::sync::CancellationToken>,
}
pub struct AuthState {
pub auth_pass: Option<AuthPass>,
pub oauth_flow: OAuthFlowTracker,
pub background_refresh_token: Option<tokio_util::sync::CancellationToken>,
}
@@ -30,7 +21,6 @@ impl Default for AuthState {
fn default() -> Self {
Self {
auth_pass: None,
oauth_flow: OAuthFlowTracker::default(),
background_refresh_token: None,
}
}
@@ -48,13 +38,12 @@ pub fn init_auth_state() -> AuthState {
AuthState {
auth_pass,
oauth_flow: OAuthFlowTracker::default(),
background_refresh_token: None,
}
}
/// Returns the auth pass object, including access token, refresh token, and metadata.
/// Automatically refreshes if expired and clears session if refresh token is expired.
/// Returns the auth pass object, including access token and metadata.
/// Automatically refreshes if expired and clears session on refresh failure.
pub async fn get_auth_pass_with_refresh() -> Option<AuthPass> {
info!("Retrieving tokens");
let Some(auth_pass) = ({ lock_r!(FDOLL).auth.auth_pass.clone() }) else {
@@ -68,24 +57,12 @@ pub async fn get_auth_pass_with_refresh() -> Option<AuthPass> {
};
let current_time = SystemTime::now().duration_since(UNIX_EPOCH).ok()?.as_secs();
let expired = current_time - issued_at >= auth_pass.expires_in;
let refresh_expired = current_time - issued_at >= auth_pass.refresh_expires_in;
let expires_at = issued_at.saturating_add(auth_pass.expires_in);
let expired = current_time >= expires_at;
if !expired {
return Some(auth_pass);
}
if refresh_expired {
info!("Refresh token expired, clearing auth state");
lock_w!(FDOLL).auth.auth_pass = None;
if let Err(e) = clear_auth_pass() {
error!("Failed to clear expired auth pass: {}", e);
}
destruct_user_session().await;
open_welcome_window();
return None;
}
let _guard = REFRESH_LOCK.lock().await;
let auth_pass = lock_r!(FDOLL).auth.auth_pass.clone()?;
@@ -95,33 +72,23 @@ pub async fn get_auth_pass_with_refresh() -> Option<AuthPass> {
lock_w!(FDOLL).auth.auth_pass = None;
return None;
};
let expired = current_time - issued_at >= auth_pass.expires_in;
let refresh_expired = current_time - issued_at >= auth_pass.refresh_expires_in;
if refresh_expired {
info!("Refresh token expired, clearing auth state after refresh lock");
lock_w!(FDOLL).auth.auth_pass = None;
if let Err(e) = clear_auth_pass() {
error!("Failed to clear expired auth pass: {}", e);
}
destruct_user_session().await;
open_welcome_window();
return None;
}
let expires_at = issued_at.saturating_add(auth_pass.expires_in);
let expired = current_time >= expires_at;
if !expired {
return Some(auth_pass);
}
info!("Access token expired, attempting refresh");
match refresh_token(&auth_pass.refresh_token).await {
match refresh_token(&auth_pass.access_token).await {
Ok(new_pass) => Some(new_pass),
Err(e) => {
error!("Failed to refresh token: {}", e);
lock_w!(FDOLL).auth.auth_pass = None;
if let Err(e) = clear_auth_pass() {
error!("Failed to clear auth pass after refresh failure: {}", e);
error!("Failed to clear expired auth pass: {}", e);
}
destruct_user_session().await;
open_welcome_window();
None
}
}
@@ -141,17 +108,6 @@ async fn refresh_if_expiring_soon() {
Err(_) => return,
};
let refresh_expires_at = issued_at.saturating_add(auth_pass.refresh_expires_in);
if current_time >= refresh_expires_at {
lock_w!(FDOLL).auth.auth_pass = None;
if let Err(e) = clear_auth_pass() {
error!("Failed to clear expired auth pass: {}", e);
}
destruct_user_session().await;
open_welcome_window();
return;
}
let access_expires_at = issued_at.saturating_add(auth_pass.expires_in);
if access_expires_at.saturating_sub(current_time) >= 60 {
return;
@@ -172,24 +128,19 @@ async fn refresh_if_expiring_soon() {
Err(_) => return,
};
let refresh_expires_at = latest_issued_at.saturating_add(latest_pass.refresh_expires_in);
if current_time >= refresh_expires_at {
lock_w!(FDOLL).auth.auth_pass = None;
if let Err(e) = clear_auth_pass() {
error!("Failed to clear expired auth pass: {}", e);
}
destruct_user_session().await;
open_welcome_window();
return;
}
let access_expires_at = latest_issued_at.saturating_add(latest_pass.expires_in);
if access_expires_at.saturating_sub(current_time) >= 60 {
return;
}
if let Err(e) = refresh_token(&latest_pass.refresh_token).await {
if let Err(e) = refresh_token(&latest_pass.access_token).await {
warn!("Background refresh failed: {}", e);
lock_w!(FDOLL).auth.auth_pass = None;
if let Err(e) = clear_auth_pass() {
error!("Failed to clear auth pass after refresh failure: {}", e);
}
destruct_user_session().await;
open_welcome_window();
}
}