Skip to content

Commit

Permalink
Port from axum-sessions to tower-sessions (#389)
Browse files Browse the repository at this point in the history
  • Loading branch information
Cogitri authored Nov 22, 2023
1 parent b6d1dc4 commit a0cb1e8
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 33 deletions.
6 changes: 2 additions & 4 deletions tutorial/server/axum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,15 @@ license = "MPL-2.0"
tracing.workspace = true
tracing-subscriber.workspace = true
serde.workspace = true
rand = { workspace = true, features = ["min_const_gen"] }
webauthn-rs = { workspace = true, features = ["danger-allow-state-serialisation"] }
axum = {version = "0.6.1", features = ["http2"]}
axum-extra = { version = "0.4.2" , features = ["spa"]}
tokio = { workspace = true, features = ["full"] }
uuid = { workspace = true, features=["v4"] }
url.workspace = true
thiserror.workspace = true
axum-sessions = "0.4.1"
tower = "0.4.13"
tower-http = {version="0.3.4", features=["fs"]}
tower-http = {version="0.4.4", features=["fs"]}
tower-sessions = "0.6"

[features]
default = ["wasm"]
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion tutorial/server/axum/build_wasm.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/bin/sh
wasm-pack build ../../wasm --out-dir ../server/axum/assets --target web
wasm-pack build ../../wasm --out-dir ../server/axum/assets/wasm --target web
22 changes: 11 additions & 11 deletions tutorial/server/axum/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use axum::{
http::StatusCode,
response::IntoResponse,
};
use axum_sessions::extractors::WritableSession;
use tower_sessions::Session;

/*
* Webauthn RS auth handlers.
Expand Down Expand Up @@ -51,7 +51,7 @@ use webauthn_rs::prelude::*;

pub async fn start_register(
Extension(app_state): Extension<AppState>,
mut session: WritableSession,
session: Session,
Path(username): Path<String>,
) -> Result<impl IntoResponse, WebauthnError> {
info!("Start register");
Expand All @@ -76,7 +76,7 @@ pub async fn start_register(
};

// Remove any previous registrations that may have occured from the session.
session.remove("reg_state");
session.remove_value("reg_state");

// If the user has any other credentials, we exclude these here so they can't be duplicate registered.
// It also hints to the browser that only new credentials should be "blinked" for interaction.
Expand Down Expand Up @@ -118,14 +118,14 @@ pub async fn start_register(

pub async fn finish_register(
Extension(app_state): Extension<AppState>,
mut session: WritableSession,
session: Session,
Json(reg): Json<RegisterPublicKeyCredential>,
) -> Result<impl IntoResponse, WebauthnError> {
let (username, user_unique_id, reg_state): (String, Uuid, PasskeyRegistration) = session
.get("reg_state")
.get("reg_state")?
.ok_or(WebauthnError::CorruptSession)?; //Corrupt Session

session.remove("reg_state");
session.remove_value("reg_state");

let res = match app_state
.webauthn
Expand Down Expand Up @@ -185,15 +185,15 @@ pub async fn finish_register(

pub async fn start_authentication(
Extension(app_state): Extension<AppState>,
mut session: WritableSession,
session: Session,
Path(username): Path<String>,
) -> Result<impl IntoResponse, WebauthnError> {
info!("Start Authentication");
// We get the username from the URL, but you could get this via form submission or
// some other process.

// Remove any previous authentication that may have occured from the session.
session.remove("auth_state");
session.remove_value("auth_state");

// Get the set of keys that the user possesses
let users_guard = app_state.users.lock().await;
Expand Down Expand Up @@ -241,14 +241,14 @@ pub async fn start_authentication(

pub async fn finish_authentication(
Extension(app_state): Extension<AppState>,
mut session: WritableSession,
session: Session,
Json(auth): Json<PublicKeyCredential>,
) -> Result<impl IntoResponse, WebauthnError> {
let (user_unique_id, auth_state): (Uuid, PasskeyAuthentication) = session
.get("auth_state")
.get("auth_state")?
.ok_or(WebauthnError::CorruptSession)?;

session.remove("auth_state");
session.remove_value("auth_state");

let res = match app_state
.webauthn
Expand Down
3 changes: 3 additions & 0 deletions tutorial/server/axum/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ pub enum WebauthnError {
UserNotFound,
#[error("User Has No Credentials")]
UserHasNoCredentials,
#[error("Deserialising Session failed: {0}")]
InvalidSessionState(#[from] tower_sessions::session::Error),
}
impl IntoResponse for WebauthnError {
fn into_response(self) -> Response {
Expand All @@ -22,6 +24,7 @@ impl IntoResponse for WebauthnError {
WebauthnError::UserNotFound => "User Not Found",
WebauthnError::Unknown => "Unknown Error",
WebauthnError::UserHasNoCredentials => "User Has No Credentials",
WebauthnError::InvalidSessionState(_) => "Deserialising Session failed",
};

// its often easiest to implement `IntoResponse` by calling other implementations
Expand Down
45 changes: 28 additions & 17 deletions tutorial/server/axum/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
use axum::{extract::Extension, routing::post, Router};
use axum_extra::routing::SpaRouter;
use axum_sessions::{async_session::MemoryStore, SameSite, SessionLayer};
use axum::{
error_handling::HandleErrorLayer, extract::Extension, http::StatusCode, routing::post,
BoxError, Router,
};
use std::net::SocketAddr;
use tower::ServiceBuilder;
use tower_sessions::{
cookie::{time::Duration, SameSite},
Expiry, MemoryStore, SessionManagerLayer,
};

mod error;
/*
* Webauthn RS server side tutorial.
Expand All @@ -12,8 +19,6 @@ mod error;
use crate::auth::{finish_authentication, finish_register, start_authentication, start_register};
use crate::startup::AppState;

use rand::prelude::*;

#[macro_use]
extern crate tracing;

Expand All @@ -37,13 +42,18 @@ async fn main() {
// Create the app
let app_state = AppState::new();

//Configure cookie based sessions
let store = MemoryStore::new();
let secret = thread_rng().gen::<[u8; 128]>(); // MUST be at least 64 bytes!
let session_layer = SessionLayer::new(store, &secret)
.with_cookie_name("webauthnrs")
.with_same_site_policy(SameSite::Lax)
.with_secure(false); // TODO: change this to true when running on an HTTPS/production server instead of locally
let session_store = MemoryStore::default();
let session_service = ServiceBuilder::new()
.layer(HandleErrorLayer::new(|_: BoxError| async {
StatusCode::BAD_REQUEST
}))
.layer(
SessionManagerLayer::new(session_store)
.with_name("webauthnrs")
.with_same_site(SameSite::Lax)
.with_secure(false) // TODO: change this to true when running on an HTTPS/production server instead of locally
.with_expiry(Expiry::OnInactivity(Duration::seconds(360))),
);

// build our application with a route
let app = Router::new()
Expand All @@ -52,17 +62,18 @@ async fn main() {
.route("/login_start/:username", post(start_authentication))
.route("/login_finish", post(finish_authentication))
.layer(Extension(app_state))
.layer(session_layer);
.layer(session_service);

#[cfg(feature = "wasm")]
let app = Router::new()
.merge(app)
.merge(SpaRouter::new("/assets", "assets").index_file("wasm_index.html"));
let app = Router::new().merge(app).nest_service(
"/assets",
tower_http::services::ServeDir::new("assets/wasm"),
);

#[cfg(feature = "javascript")]
let app = Router::new()
.merge(app)
.merge(SpaRouter::new("/assets", "assets").index_file("js_index.html"));
.nest_service("/assets", tower_http::services::ServeDir::new("assets/js"));

// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
Expand Down

0 comments on commit a0cb1e8

Please sign in to comment.