diff --git a/src/interfaces/api/handlers/auth_handler.rs b/src/interfaces/api/handlers/auth_handler.rs index f4007a66..4d66b474 100755 --- a/src/interfaces/api/handlers/auth_handler.rs +++ b/src/interfaces/api/handlers/auth_handler.rs @@ -674,4 +674,4 @@ async fn oidc_exchange( ); cookie_auth::append_csrf_cookie(response.headers_mut(), auth_response.expires_in); Ok(response) -} \ No newline at end of file +} diff --git a/src/interfaces/api/handlers/file_handler.rs b/src/interfaces/api/handlers/file_handler.rs index 495b0a14..296f7504 100755 --- a/src/interfaces/api/handlers/file_handler.rs +++ b/src/interfaces/api/handlers/file_handler.rs @@ -106,7 +106,11 @@ impl FileHandler { if let Some(ref fid) = folder_id { use crate::application::ports::inbound::FolderUseCase; let folder_service = &state.applications.folder_service; - if folder_service.get_folder_owned(fid, &auth_user.id).await.is_err() { + if folder_service + .get_folder_owned(fid, &auth_user.id) + .await + .is_err() + { tracing::warn!( "⛔ UPLOAD REJECTED (IDOR): user='{}' attempted upload to folder '{}' owned by another user", auth_user.username, @@ -366,10 +370,8 @@ impl FileHandler { .unwrap() .into_response() } - Err(err) => { - AppError::internal_error(format!("Thumbnail generation failed: {}", err)) - .into_response() - } + Err(err) => AppError::internal_error(format!("Thumbnail generation failed: {}", err)) + .into_response(), } } @@ -542,9 +544,7 @@ impl FileHandler { .unwrap() .into_response(), }, - Err(err) => { - AppError::from(err).into_response() - } + Err(err) => AppError::from(err).into_response(), } } @@ -594,9 +594,7 @@ impl FileHandler { .insert(header::ETAG, header::HeaderValue::from_str(&etag).unwrap()); resp } - Err(err) => { - AppError::from(err).into_response() - } + Err(err) => AppError::from(err).into_response(), } } @@ -635,8 +633,7 @@ impl FileHandler { let file_path = state.core.dedup_service.blob_path(&blob_hash); tokio::spawn(async move { tracing::info!("🖼️ Generating thumbnails for: {}", file_id); - thumbnail_service - .generate_all_sizes_background(file_id, file_path); + thumbnail_service.generate_all_sizes_background(file_id, file_path); }); } Err(e) => { @@ -726,7 +723,7 @@ impl FileHandler { match result { Ok(_) => StatusCode::NO_CONTENT.into_response(), - Err(err) => AppError::from(err).into_response() + Err(err) => AppError::from(err).into_response(), } } @@ -758,7 +755,7 @@ impl FileHandler { let mgmt = &state.applications.file_management_service; match mgmt.rename_file_owned(&id, &auth_user.id, &new_name).await { Ok(file_dto) => (StatusCode::OK, Json(file_dto)).into_response(), - Err(err) => AppError::from(err).into_response() + Err(err) => AppError::from(err).into_response(), } } @@ -778,7 +775,7 @@ impl FileHandler { .await { Ok(file) => (StatusCode::OK, Json(file)).into_response(), - Err(err) => AppError::from(err).into_response() + Err(err) => AppError::from(err).into_response(), } } @@ -797,7 +794,7 @@ impl FileHandler { let mgmt = &state.applications.file_management_service; match mgmt.move_file_owned(&id, &auth_user.id, folder_id).await { Ok(file_dto) => (StatusCode::OK, Json(file_dto)).into_response(), - Err(err) => AppError::from(err).into_response() + Err(err) => AppError::from(err).into_response(), } } @@ -856,9 +853,7 @@ impl FileHandler { }) .collect(); - format!( - "{disposition}; filename=\"{ascii_safe}\"; filename*=UTF-8''{encoded}" - ) + format!("{disposition}; filename=\"{ascii_safe}\"; filename*=UTF-8''{encoded}") } /// Build a 201 Created JSON response. diff --git a/static/js/app/authSession.js b/static/js/app/authSession.js index ae64bf38..8f3f3979 100755 --- a/static/js/app/authSession.js +++ b/static/js/app/authSession.js @@ -92,24 +92,36 @@ async function checkAuthentication() { window.updateStorageUsageDisplay(userData); - refreshUserData().then(freshData => { - if (freshData) { - console.log('Storage usage updated from server'); - } else { - // Session expired — try silent refresh first - console.warn('Session may have expired, trying refresh...'); - fetch('/api/auth/refresh', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', ...getCsrfHeaders() }, body: '{}' }) - .then(r => r.ok ? refreshUserData() : Promise.reject(new Error('refresh failed'))) - .catch(() => { - localStorage.removeItem(USER_DATA_KEY); - window.location.href = '/login?source=session_expired'; - }); + // Validate session BEFORE loading files to avoid 401 race condition + const freshData = await refreshUserData(); + if (freshData) { + console.log('Storage usage updated from server'); + } else { + // Session expired — try silent refresh first + console.warn('Session may have expired, trying refresh...'); + try { + const r = await fetch('/api/auth/refresh', { + method: 'POST', + credentials: 'same-origin', + headers: { 'Content-Type': 'application/json', ...getCsrfHeaders() }, + body: '{}' + }); + if (r.ok) { + await refreshUserData(); + } else { + localStorage.removeItem(USER_DATA_KEY); + window.location.href = '/login?source=session_expired'; + return; + } + } catch (err) { + localStorage.removeItem(USER_DATA_KEY); + window.location.href = '/login?source=session_expired'; + return; } - }).catch(err => { - console.warn('Could not refresh user data:', err); - }); + } - resolveHomeFolder().then(() => window.loadFiles()); + await resolveHomeFolder(); + window.loadFiles(); } else { // No cached user data — must verify session from server console.log('No cached user data, fetching from server');