Skip to content

Commit 6f61847

Browse files
migrate to tower-sessions (#14)
This migrates from axum-sessions to tower-sessions. Some notable differences: 1. Readable and writable sessions are replaced with a single session type. 2. Sessions no longer contain data and instead are a pointer to a session; this means we don't need a secret key anymore. 3. serde_json::Value is accessible directly. 4. The session layer itself is fallible, so we use handle error layer to ensure compatibility with axum. Closes #13.
1 parent d2ad9c7 commit 6f61847

File tree

8 files changed

+129
-473
lines changed

8 files changed

+129
-473
lines changed

Cargo.lock

+87-444
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

back_end/Cargo.toml

+4-2
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ readme = "README.md"
1111
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1212

1313
[dependencies]
14-
axum = { version="0.6.20", features = ["headers"] }
14+
axum = { version = "0.6.20", features = ["headers"] }
15+
tower = "0.4.13"
1516
tower-http = { version = "0.4", features = ["full"] }
16-
axum-sessions = "0.5"
17+
tower-sessions = "0.2.2"
1718
tokio = { version = "1.24", features = ["full"] }
1819
tracing = "0.1"
1920
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
2021
serde = { version = "1.0", features = ["derive"] }
22+
serde_json = "1.0.107"

back_end/src/main.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
#![allow(missing_docs)]
55

66
use axum::Router;
7-
use axum_sessions::{async_session::MemoryStore, SessionLayer};
87
use std::net::SocketAddr;
98
use std::{env, sync::Arc};
9+
use tower_sessions::{MemoryStore, SessionManagerLayer};
1010
use tracing::log::warn;
1111
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
1212

@@ -35,7 +35,7 @@ async fn main() {
3535
.init();
3636

3737
// configure server from environmental variables
38-
let (port, host, secret) = from_env();
38+
let (port, host) = from_env();
3939

4040
let addr: SocketAddr = format!("{}:{}", host, port)
4141
.parse()
@@ -45,8 +45,8 @@ async fn main() {
4545
let shared_state = Arc::new(store::Store::new("123456789"));
4646

4747
// setup up sessions and store to keep track of session information
48-
let session_layer = SessionLayer::new(MemoryStore::new(), secret.as_bytes())
49-
.with_cookie_name(SESSION_COOKIE_NAME);
48+
let session_store = MemoryStore::default();
49+
let session_layer = SessionManagerLayer::new(session_store).with_name(SESSION_COOKIE_NAME);
5050

5151
// combine the front and backend into server
5252
let app = Router::new()
@@ -73,7 +73,7 @@ async fn shutdown_signal() {
7373

7474
// Variables from Environment or default to configure server
7575
// port, host, secret
76-
fn from_env() -> (String, String, String) {
76+
fn from_env() -> (String, String) {
7777
if env::var("SERVER_SECRET").is_err() {
7878
warn!("env var SERVER_SECRET should be set and unique (64 bytes long)");
7979
}
@@ -84,9 +84,5 @@ fn from_env() -> (String, String, String) {
8484
env::var("SERVER_HOST")
8585
.ok()
8686
.unwrap_or_else(|| SERVER_HOST.to_string()),
87-
env::var("SERVER_SECRET").ok().unwrap_or_else(|| {
88-
"this needs to be 64bytes. recommended that you set Secret instead of fixed value"
89-
.to_string()
90-
}),
9187
)
9288
}

back_end/src/middlewares/usersecure.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ use axum::{
33
middleware::Next,
44
response::Response,
55
};
6-
use axum_sessions::extractors::ReadableSession;
6+
use tower_sessions::Session;
77

88
#[allow(clippy::missing_errors_doc)]
99
pub async fn user_secure<B: Send>(
10-
session: ReadableSession,
10+
session: Session,
1111
req: Request<B>,
1212
next: Next<B>,
1313
) -> Result<Response, StatusCode> {
1414
tracing::info!("Middleware: checking if user exists");
15-
let user_id = session.get_raw("user_id").ok_or(StatusCode::UNAUTHORIZED)?;
15+
let user_id = session
16+
.get_value("user_id")
17+
.ok_or(StatusCode::UNAUTHORIZED)?;
1618
tracing::debug!("user_id Extracted: {}", user_id);
1719

1820
// accepts all user but you could add a check here to match user access

back_end/src/routes/api.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use axum::{response::IntoResponse, Json};
2-
use axum_sessions::async_session::serde_json::json;
2+
use serde_json::json;
33

44
/// imitating an API response
55
#[allow(clippy::unused_async)]

back_end/src/routes/auth.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use axum::{response::IntoResponse, Json};
2-
use axum_sessions::{async_session::serde_json::json, extractors::WritableSession};
32
use serde::Deserialize;
3+
use serde_json::json;
4+
use tower_sessions::Session;
45

56
/// route to handle log in
67
#[allow(clippy::unused_async)]
78
#[allow(clippy::missing_panics_doc)]
8-
pub async fn login(mut session: WritableSession, Json(login): Json<Login>) -> impl IntoResponse {
9+
pub async fn login(session: Session, Json(login): Json<Login>) -> impl IntoResponse {
910
tracing::info!("Logging in user: {}", login.username);
1011

1112
if check_password(&login.username, &login.password) {
@@ -18,11 +19,11 @@ pub async fn login(mut session: WritableSession, Json(login): Json<Login>) -> im
1819

1920
/// route to handle log out
2021
#[allow(clippy::unused_async)]
21-
pub async fn logout(mut session: WritableSession) -> impl IntoResponse {
22-
let user = session.get_raw("user_id").unwrap_or_default();
22+
pub async fn logout(session: Session) -> impl IntoResponse {
23+
let user = session.get_value("user_id").unwrap_or_default();
2324
tracing::info!("Logging out user: {}", user);
2425
// drop session
25-
session.destroy();
26+
session.flush();
2627
Json(json!({"result": "ok"}))
2728
}
2829

back_end/src/routes/session.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
// print out session
22

33
use axum::{response::IntoResponse, Json};
4-
use axum_sessions::{async_session::serde_json::json, extractors::ReadableSession};
4+
use serde_json::json;
5+
use tower_sessions::Session;
56

67
/// output entire session object
78
#[allow(clippy::unused_async)]
8-
pub async fn handler(session: ReadableSession) -> impl IntoResponse {
9+
pub async fn handler(session: Session) -> impl IntoResponse {
910
tracing::info!("Seeking session info");
1011
Json(json!({ "session": format!("{:?}", session) }))
1112
}
1213

1314
/// output session data in json
1415
#[allow(clippy::unused_async)]
15-
pub async fn data_handler(session: ReadableSession) -> impl IntoResponse {
16+
pub async fn data_handler(session: Session) -> impl IntoResponse {
1617
tracing::info!("Seeking session data");
17-
let user_id = session.get("user_id").unwrap_or_else(|| "".to_string());
18+
let user_id = session.get_value("user_id").unwrap_or_else(|| "".into());
1819
Json(json!({ "user_id": user_id }))
1920
}

back_end/src/services.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
use axum::{
2+
error_handling::HandleErrorLayer,
3+
handler::HandlerWithoutStateExt,
24
http::StatusCode,
35
middleware,
46
routing::{get, post},
5-
Router, handler::HandlerWithoutStateExt,
7+
BoxError, Router,
68
};
7-
use axum_sessions::{async_session::SessionStore, SessionLayer};
89
use std::sync::Arc;
10+
use tower::ServiceBuilder;
911
use tower_http::{services::ServeDir, trace::TraceLayer};
12+
use tower_sessions::{SessionManagerLayer, SessionStore};
1013

1114
use crate::{
1215
middlewares, routes,
@@ -20,7 +23,9 @@ use crate::{
2023
// Front end to server svelte build bundle, css and index.html from public folder
2124
pub fn front_public_route() -> Router {
2225
Router::new()
23-
.fallback_service(ServeDir::new(FRONT_PUBLIC).not_found_service(handle_error.into_service()))
26+
.fallback_service(
27+
ServeDir::new(FRONT_PUBLIC).not_found_service(handle_error.into_service()),
28+
)
2429
.layer(TraceLayer::new_for_http())
2530
}
2631

@@ -37,16 +42,22 @@ async fn handle_error() -> (StatusCode, &'static str) {
3742
// ********
3843
// Back end server built form various routes that are either public, require auth, or secure login
3944
pub fn backend<Store: SessionStore>(
40-
session_layer: SessionLayer<Store>,
45+
session_layer: SessionManagerLayer<Store>,
4146
shared_state: Arc<store::Store>,
4247
) -> Router {
48+
let session_service = ServiceBuilder::new()
49+
.layer(HandleErrorLayer::new(|_: BoxError| async {
50+
StatusCode::BAD_REQUEST
51+
}))
52+
.layer(session_layer);
53+
4354
// could add tower::ServiceBuilder here to group layers, especially if you add more layers.
4455
// see https://docs.rs/axum/latest/axum/middleware/index.html#ordering
4556
Router::new()
4657
.merge(back_public_route())
4758
.merge(back_auth_route())
4859
.merge(back_token_route(shared_state))
49-
.layer(session_layer)
60+
.layer(session_service)
5061
}
5162

5263
// *********

0 commit comments

Comments
 (0)