From 8db5c43134641b3db0e0c4f977abf8bef16e8067 Mon Sep 17 00:00:00 2001 From: Henning Date: Thu, 15 Jan 2026 22:17:19 +0100 Subject: [PATCH] Change OAuth2 expiration handling to use i64 for timestamps --- src/api/admin.rs | 8 +++----- src/mail.rs | 14 +++++--------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/api/admin.rs b/src/api/admin.rs index 7dcba847..57bf0ce8 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -106,7 +106,7 @@ static CAN_BACKUP: LazyLock = static CAN_BACKUP: LazyLock = LazyLock::new(|| false); // OAuth2 state storage for CSRF protection (state -> expiration timestamp) -static OAUTH2_STATES: LazyLock>> = LazyLock::new(|| RwLock::new(HashMap::new())); +static OAUTH2_STATES: LazyLock>> = LazyLock::new(|| RwLock::new(HashMap::new())); #[get("/")] fn admin_disabled() -> &'static str { @@ -372,9 +372,7 @@ fn oauth2_authorize(_token: AdminToken) -> Result { let state = crate::crypto::encode_random_bytes::<32>(&BASE64URL_NOPAD); // Store state with expiration (10 minutes from now) - // Multiple calls to Utc::now().timestamp() can return a negative value if the system time is set before the UNIX epoch. - // While extremely rare in practice, we handle this by using unwrap_or_default() to prevent huge values when casting to u64. - let now = u64::try_from(Utc::now().timestamp()).unwrap_or_default(); + let now = Utc::now().timestamp(); let expiration = now + 600; OAUTH2_STATES.write().unwrap().insert(state.clone(), expiration); @@ -433,7 +431,7 @@ async fn oauth2_callback( // Validate state token let valid_state = { let states = OAUTH2_STATES.read().unwrap(); - let now = u64::try_from(Utc::now().timestamp()).unwrap_or_default(); + let now = Utc::now().timestamp(); states.get(&state).is_some_and(|&exp| exp > now) }; diff --git a/src/mail.rs b/src/mail.rs index f7d4e410..a62ced31 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -31,7 +31,7 @@ use crate::http_client::make_http_request; pub struct OAuth2Token { access_token: String, refresh_token: Option, - expires_at: Option, + expires_at: Option, token_type: String, } @@ -39,7 +39,7 @@ pub struct OAuth2Token { struct TokenRefreshResponse { access_token: String, refresh_token: Option, - expires_in: Option, + expires_in: Option, token_type: String, } @@ -78,11 +78,7 @@ pub async fn refresh_oauth2_token() -> Result { Err(e) => err!(format!("OAuth2 Token Parse Error: {e}")), }; - let expires_at = token_response.expires_in.map(|expires_in| { - // Multiple calls to Utc::now().timestamp() can return a negative value if the system time is set before the UNIX epoch. - // While extremely rare in practice, we handle this by using unwrap_or_default() to prevent huge values when casting to u64. - u64::try_from(Utc::now().timestamp()).unwrap_or_default() + expires_in - }); + let expires_at = token_response.expires_in.map(|expires_in| Utc::now().timestamp() + expires_in); let new_token = OAuth2Token { access_token: token_response.access_token, @@ -106,7 +102,7 @@ async fn get_valid_oauth2_token() -> Result { if let Some(token) = token_cache.as_ref() { // Check if token is still valid (with 5 min buffer) if let Some(expires_at) = token.expires_at { - let now = u64::try_from(Utc::now().timestamp()).unwrap_or_default(); + let now = Utc::now().timestamp(); if now + 300 < expires_at { return Ok(token.clone()); } @@ -120,7 +116,7 @@ async fn get_valid_oauth2_token() -> Result { // Double check if let Some(token) = token_cache.as_ref() { if let Some(expires_at) = token.expires_at { - let now = u64::try_from(Utc::now().timestamp()).unwrap_or_default(); + let now = Utc::now().timestamp(); if now + 300 < expires_at { return Ok(token.clone()); }