use std::sync::Arc; use axum::Extension; use axum::extract::{Request, State}; use axum::http::StatusCode; use axum::middleware::Next; use axum::response::{IntoResponse, Response}; use export::AppState; use crate::oauth::{append_cookie, read_cookie_header}; const SKIP_PREFIXES: [&str; 8] = [ "/healthz", "/readyz", "/api/docs", "/api/v1/auth/", "/api/v1/internal", "/assets", "/pages", "/swagger", ]; pub async fn revalidate( State(state): State, Extension(verifier): Extension>, request: Request, next: Next, ) -> Response { let path = request.uri().path(); if SKIP_PREFIXES.iter().any(|prefix| path.starts_with(prefix)) { return next.run(request).await; } let Some(token) = read_cookie_header(request.headers(), &state.config.oauth.session_cookie) .map(str::to_owned) else { return next.run(request).await; }; let Ok(claims) = verifier.verify_access(&token).await else { return next.run(request).await; }; let Some(session_id) = claims.sid.as_deref() else { return next.run(request).await; }; let Some(kenauth) = state.kenauth.as_ref() else { return next.run(request).await; }; let key = format!("oauth:recheck:{}:{}", session_id, claims.security_version); if export::redis::get_string(&state.redis, &key) .await .ok() .flatten() .is_some() { return next.run(request).await; } match kenauth .get_session(claims.sub, claims.security_version, session_id) .await { Ok(_) => { let _ = export::redis::set_string( &state.redis, &key, "1", state.config.oauth.recheck_secs, ) .await; next.run(request).await } Err(kenauth::KenAuthError::Unauthenticated) | Err(kenauth::KenAuthError::NotFound) => { let mut response = ( StatusCode::UNAUTHORIZED, axum::Json(serde_json::json!({ "code": 401, "message": "session_revoked", "data": null, "error": null, "error_code": "session_revoked", "request_id": null, "log_id": null, })), ) .into_response(); append_cookie(&mut response, &jwt::clear_session_cookie(&state.config.oauth)); response } Err(_) => next.run(request).await, } }