refactor: extract session_id extraction helper, DRY auth.rs

Extract duplicated session ID extraction + validation logic from
SessionAuthMiddleware::call() and require_session() into two shared
helper functions: extract_session_id and validate_and_build_identity.

Removes ~60 lines of duplicated code while preserving behavior:
- Both call sites now rely on the same extraction/validation path
- User-Agent default remains empty string (existing behavior unchanged)
- Error response format (401 with header/validation messages) unchanged

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
asepharyana
2026-07-18 03:18:27 +07:00
co-authored by Claude Opus 4.8
parent 7d8487cefb
commit e32501ee59
2 changed files with 1037 additions and 56 deletions
+62 -56
View File
@@ -115,6 +115,52 @@ pub struct SessionAuthMiddleware<S> {
store: Arc<Store>,
}
// ---------------------------------------------------------------------------
// Session ID helpers
// ---------------------------------------------------------------------------
/// Extract and validate `X-Session-Id` from request headers.
///
/// Flow: read header -> validate non-empty -> return ID or a 401 error response.
fn extract_session_id<ReqBody>(req: &Request<ReqBody>) -> Result<String, Response> {
let session_id = req
.headers()
.get("X-Session-Id")
.and_then(|v| v.to_str().ok())
.map(|s| s.to_string());
match session_id {
Some(id) if !id.is_empty() => Ok(id),
_ => Err((StatusCode::UNAUTHORIZED, "missing X-Session-Id header").into_response()),
}
}
/// Validate session and build identity from request context.
///
/// Flow: validate session in store -> extract User-Agent -> build SessionIdentity.
fn validate_and_build_identity<ReqBody>(
session_id: &str,
store: &Store,
req: &Request<ReqBody>,
) -> Result<SessionIdentity, Response> {
match validate_session(session_id, store) {
Ok(_session) => {
let user_agent = req
.headers()
.get(header::USER_AGENT)
.and_then(|v| v.to_str().ok())
.unwrap_or("")
.to_string();
Ok(SessionIdentity::new(session_id.to_string(), user_agent))
}
Err(e) => Err((
StatusCode::UNAUTHORIZED,
format!("session validation failed: {e}"),
)
.into_response()),
}
}
impl<S, ReqBody> Service<Request<ReqBody>> for SessionAuthMiddleware<S>
where
S: Service<Request<ReqBody>, Response = Response> + Send + 'static,
@@ -133,39 +179,17 @@ where
fn call(&mut self, mut req: Request<ReqBody>) -> Self::Future {
let store = Arc::clone(&self.store);
// Extract session id from header.
let session_id = req
.headers()
.get("X-Session-Id")
.and_then(|v| v.to_str().ok())
.map(|s| s.to_string());
let session_id = match session_id {
Some(id) if !id.is_empty() => id,
_ => {
let resp =
(StatusCode::UNAUTHORIZED, "missing X-Session-Id header").into_response();
return Box::pin(async move { Ok(resp) });
}
let session_id = match extract_session_id(&req) {
Ok(id) => id,
Err(resp) => return Box::pin(async move { Ok(resp) }),
};
// Validate session.
if let Err(e) = validate_session(&session_id, &store) {
let resp = (
StatusCode::UNAUTHORIZED,
format!("session validation failed: {e}"),
)
.into_response();
return Box::pin(async move { Ok(resp) });
}
let user_agent = req
.headers()
.get(header::USER_AGENT)
.and_then(|v| v.to_str().ok())
.unwrap_or("")
.to_string();
let identity = SessionIdentity::new(session_id, user_agent);
req.extensions_mut().insert(identity);
match validate_and_build_identity(&session_id, &store, &req) {
Ok(identity) => {
req.extensions_mut().insert(identity);
}
Err(resp) => return Box::pin(async move { Ok(resp) }),
};
let fut = self.inner.call(req);
Box::pin(fut)
@@ -186,33 +210,15 @@ pub async fn require_session(
mut req: Request<axum::body::Body>,
next: axum::middleware::Next,
) -> Response {
let session_id = req
.headers()
.get("X-Session-Id")
.and_then(|v| v.to_str().ok())
.map(|s| s.to_string());
let session_id = match session_id {
Some(id) if !id.is_empty() => id,
_ => {
return (StatusCode::UNAUTHORIZED, "missing X-Session-Id header").into_response();
}
let session_id = match extract_session_id(&req) {
Ok(id) => id,
Err(resp) => return resp,
};
if let Err(e) = validate_session(&session_id, &store) {
return (
StatusCode::UNAUTHORIZED,
format!("session validation failed: {e}"),
)
.into_response();
}
let user_agent = req
.headers()
.get(header::USER_AGENT)
.and_then(|v| v.to_str().ok())
.unwrap_or("")
.to_string();
let identity = SessionIdentity::new(session_id, user_agent);
let identity = match validate_and_build_identity(&session_id, &store, &req) {
Ok(identity) => identity,
Err(resp) => return resp,
};
req.extensions_mut().insert(identity);
next.run(req).await
}