use autumn_web::session::{MemoryStore, Session, SessionConfig, SessionLayer, SessionStore};
use axum::{
Router,
body::Body,
http::{Request, StatusCode},
routing::get,
};
use tower::ServiceExt;
#[tokio::test]
async fn test_session_fixation() {
async fn login_handler(session: Session) -> &'static str {
session.rotate_id().await;
session.insert("user_id", "123").await;
"logged in"
}
let store = MemoryStore::new();
let config = SessionConfig::default();
let state = autumn_web::AppState::for_test();
let app = Router::new()
.route("/login", get(login_handler))
.layer(SessionLayer::new(store.clone(), config))
.with_state(state);
let attacker_session_id = "attacker-provided-id";
let mut initial_data = std::collections::HashMap::new();
initial_data.insert("some_data".to_string(), "attacker_set".to_string());
store.save(attacker_session_id, initial_data).await.unwrap();
let response = app
.oneshot(
Request::builder()
.method("GET")
.uri("/login")
.header("Cookie", format!("autumn.sid={attacker_session_id}"))
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let set_cookie = response
.headers()
.get("set-cookie")
.unwrap()
.to_str()
.unwrap();
assert!(
!set_cookie.contains(attacker_session_id),
"Cookie should have a new ID, but the attacker's ID was fixed!"
);
let new_id = set_cookie
.split('=')
.nth(1)
.unwrap()
.split(';')
.next()
.unwrap();
assert!(
store.load(attacker_session_id).await.unwrap().is_none(),
"Old session ID was not destroyed"
);
let new_data = store
.load(new_id)
.await
.unwrap()
.expect("New session ID should be in store");
assert_eq!(new_data.get("user_id").unwrap(), "123");
}
#[tokio::test]
async fn test_rotate_id() {
async fn rotate_handler(session: Session) -> &'static str {
session.insert("user", "alice").await;
session.rotate_id().await;
"rotated"
}
let store = MemoryStore::new();
let config = SessionConfig::default();
let state = autumn_web::AppState::for_test();
let app = Router::new()
.route("/rotate", get(rotate_handler))
.layer(SessionLayer::new(store.clone(), config))
.with_state(state);
let session_id = "initial-id-123";
let mut initial_data = std::collections::HashMap::new();
initial_data.insert("pre_existing".to_string(), "data".to_string());
store.save(session_id, initial_data).await.unwrap();
let response = app
.oneshot(
Request::builder()
.method("GET")
.uri("/rotate")
.header("Cookie", format!("autumn.sid={session_id}"))
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let set_cookie = response
.headers()
.get("set-cookie")
.unwrap()
.to_str()
.unwrap();
assert!(
!set_cookie.contains(session_id),
"Cookie should have a new ID"
);
let new_id = set_cookie
.split('=')
.nth(1)
.unwrap()
.split(';')
.next()
.unwrap();
assert!(
store.load(session_id).await.unwrap().is_none(),
"Old session ID was not destroyed"
);
let new_data = store
.load(new_id)
.await
.unwrap()
.expect("New session ID should be in store");
assert_eq!(new_data.get("pre_existing").unwrap(), "data");
assert_eq!(new_data.get("user").unwrap(), "alice");
}