From 5ee89549a3f630fa3f34c82f8535289e39ebd659 Mon Sep 17 00:00:00 2001 From: Jon Seager Date: Thu, 5 Feb 2026 18:13:34 +0000 Subject: [PATCH] refactor(logging): log errors in auth and payload parsing - Log bearer token lookup and user lookup failures in AuthenticatedUser extractor - Log session lookup and user lookup failures in session-based authentication - Log fire-and-forget token update_last_used errors - Log JSON and form payload parse failures in FlexiblePayload --- src/application/auth.rs | 38 +++++++++++++++++++++++++------ src/application/routes/support.rs | 18 ++++++++++----- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/application/auth.rs b/src/application/auth.rs index 382cb84..4492d0a 100644 --- a/src/application/auth.rs +++ b/src/application/auth.rs @@ -4,6 +4,7 @@ use axum::{ http::{StatusCode, header, request::Parts}, }; use tower_cookies::Cookies; +use tracing::warn; use crate::application::server::AppState; use crate::domain::users::User; @@ -41,7 +42,10 @@ impl FromRequestParts for AuthenticatedUser { .get(header::AUTHORIZATION) .ok_or(StatusCode::UNAUTHORIZED)?; - let auth_str = auth_header.to_str().map_err(|_| StatusCode::UNAUTHORIZED)?; + let auth_str = auth_header.to_str().map_err(|err| { + warn!(error = %err, "authorization header contains invalid characters"); + StatusCode::UNAUTHORIZED + })?; // Check for "Bearer " format let token = auth_str @@ -56,7 +60,10 @@ impl FromRequestParts for AuthenticatedUser { .token_repo .get_by_token_hash(&token_hash) .await - .map_err(|_| StatusCode::UNAUTHORIZED)?; + .map_err(|err| { + warn!(error = %err, "bearer token lookup failed"); + StatusCode::UNAUTHORIZED + })?; // Check if token is revoked if token_record.is_revoked() { @@ -67,7 +74,9 @@ impl FromRequestParts for AuthenticatedUser { let token_repo = state.token_repo.clone(); let token_id = token_record.id; tokio::spawn(async move { - let _ = token_repo.update_last_used(token_id).await; + if let Err(err) = token_repo.update_last_used(token_id).await { + warn!(error = %err, %token_id, "failed to update token last_used"); + } }); // Get the user @@ -75,7 +84,10 @@ impl FromRequestParts for AuthenticatedUser { .user_repo .get(token_record.user_id) .await - .map_err(|_| StatusCode::UNAUTHORIZED)?; + .map_err(|err| { + warn!(error = %err, user_id = %token_record.user_id, "user lookup failed for valid token"); + StatusCode::UNAUTHORIZED + })?; Ok(AuthenticatedUser(user)) } @@ -88,18 +100,30 @@ async fn authenticate_via_session(state: &AppState, cookies: &Cookies) -> Option let session_token_hash = hash_token(session_token); // Check if session exists and is valid - let session = state + let session = match state .session_repo .get_by_token_hash(&session_token_hash) .await - .ok()?; + { + Ok(s) => s, + Err(err) => { + warn!(error = %err, "session lookup failed during authentication"); + return None; + } + }; if session.is_expired() { return None; } // Get the user - state.user_repo.get(session.user_id).await.ok() + match state.user_repo.get(session.user_id).await { + Ok(user) => Some(user), + Err(err) => { + warn!(error = %err, user_id = %session.user_id, "user lookup failed for valid session"); + None + } + } } /// Helper to extract authenticated user from request extensions diff --git a/src/application/routes/support.rs b/src/application/routes/support.rs index 26afe33..6183fbe 100644 --- a/src/application/routes/support.rs +++ b/src/application/routes/support.rs @@ -4,6 +4,7 @@ use axum::extract::{Form, FromRequest, Json as JsonPayload, Request}; use axum::http::{HeaderMap, HeaderValue, header::CONTENT_TYPE}; use axum::response::{Html, IntoResponse, Response}; use serde::Deserialize; +use tracing::warn; use crate::application::errors::{ApiError, AppError}; use crate::application::server::AppState; @@ -183,9 +184,13 @@ where .to_ascii_lowercase(); if content_type.starts_with("application/json") { - let JsonPayload(payload) = JsonPayload::::from_request(req, state) - .await - .map_err(|_| ApiError::from(AppError::validation("invalid JSON payload")))?; + let JsonPayload(payload) = + JsonPayload::::from_request(req, state) + .await + .map_err(|_| { + warn!("failed to parse JSON payload"); + ApiError::from(AppError::validation("invalid JSON payload")) + })?; return Ok(Self { inner: payload, @@ -195,9 +200,10 @@ where if content_type.is_empty() || content_type.starts_with("application/x-www-form-urlencoded") { - let Form(payload) = Form::::from_request(req, state) - .await - .map_err(|_| ApiError::from(AppError::validation("invalid form payload")))?; + let Form(payload) = Form::::from_request(req, state).await.map_err(|_| { + warn!("failed to parse form payload"); + ApiError::from(AppError::validation("invalid form payload")) + })?; return Ok(Self { inner: payload,