qol(http): replaced all explicit header setting with axum cookie jar
This commit is contained in:
Generated
+34
@@ -366,6 +366,28 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-extra"
|
||||||
|
version = "0.12.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be44683b41ccb9ab2d23a5230015c9c3c55be97a25e4428366de8873103f7970"
|
||||||
|
dependencies = [
|
||||||
|
"axum",
|
||||||
|
"axum-core",
|
||||||
|
"bytes",
|
||||||
|
"cookie",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"mime",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base16ct"
|
name = "base16ct"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -682,6 +704,17 @@ version = "0.9.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cookie"
|
||||||
|
version = "0.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
"time",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.7"
|
version = "0.8.7"
|
||||||
@@ -2871,6 +2904,7 @@ dependencies = [
|
|||||||
"argon2",
|
"argon2",
|
||||||
"async-broadcast",
|
"async-broadcast",
|
||||||
"axum",
|
"axum",
|
||||||
|
"axum-extra",
|
||||||
"bytes",
|
"bytes",
|
||||||
"chrono",
|
"chrono",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
|
|||||||
@@ -37,3 +37,4 @@ rubato = "3.0.0"
|
|||||||
chrono = "0.4.45"
|
chrono = "0.4.45"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
|
axum-extra = { version = "0.12.6", features = ["cookie"] }
|
||||||
|
|||||||
+11
-38
@@ -6,6 +6,7 @@ use std::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use axum_extra::extract::{CookieJar, cookie::Cookie};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
// The Rust `regex` crate is guaranteed linear-time and therefore does NOT
|
// The Rust `regex` crate is guaranteed linear-time and therefore does NOT
|
||||||
@@ -206,22 +207,6 @@ struct CreateStreamKeyBody {
|
|||||||
label: String,
|
label: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_session_token(headers: &HeaderMap) -> Option<String> {
|
|
||||||
// Check bare `session` header first (curl / API clients)
|
|
||||||
if let Some(v) = headers.get("session") {
|
|
||||||
return Some(v.to_str().ok()?.to_string());
|
|
||||||
}
|
|
||||||
// Fall back to Cookie header (browsers)
|
|
||||||
let cookie_header = headers.get("cookie")?.to_str().ok()?;
|
|
||||||
cookie_header
|
|
||||||
.split(';')
|
|
||||||
.find_map(|pair| {
|
|
||||||
let pair = pair.trim();
|
|
||||||
pair.strip_prefix("session=")
|
|
||||||
})
|
|
||||||
.map(|v| v.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AuthUser(entity::users::Model);
|
struct AuthUser(entity::users::Model);
|
||||||
|
|
||||||
impl FromRequestParts<Arc<HttpServer>> for AuthUser {
|
impl FromRequestParts<Arc<HttpServer>> for AuthUser {
|
||||||
@@ -231,9 +216,10 @@ impl FromRequestParts<Arc<HttpServer>> for AuthUser {
|
|||||||
parts: &mut Parts,
|
parts: &mut Parts,
|
||||||
state: &Arc<HttpServer>,
|
state: &Arc<HttpServer>,
|
||||||
) -> Result<Self, HttpError> {
|
) -> Result<Self, HttpError> {
|
||||||
let token = extract_session_token(&parts.headers).ok_or(HttpError::Unauthorized)?;
|
let jar = CookieJar::from_request_parts(parts, state).await.unwrap();
|
||||||
|
let session = jar.get("session").ok_or(HttpError::Unauthorized)?;
|
||||||
|
|
||||||
let user = users::Entity::find_by_auth_session(&state.db, token)
|
let user = users::Entity::find_by_auth_session(&state.db, session.to_string())
|
||||||
.await?
|
.await?
|
||||||
.ok_or(HttpError::Unauthorized)?;
|
.ok_or(HttpError::Unauthorized)?;
|
||||||
|
|
||||||
@@ -306,6 +292,7 @@ struct LoginResponse {
|
|||||||
async fn login_handler(
|
async fn login_handler(
|
||||||
State(state): State<Arc<HttpServer>>,
|
State(state): State<Arc<HttpServer>>,
|
||||||
Json(payload): Json<LoginForm>,
|
Json(payload): Json<LoginForm>,
|
||||||
|
jar: CookieJar,
|
||||||
) -> Result<impl IntoResponse, HttpError> {
|
) -> Result<impl IntoResponse, HttpError> {
|
||||||
let user = users::Entity::find_by_username(&state.db, payload.username.clone())
|
let user = users::Entity::find_by_username(&state.db, payload.username.clone())
|
||||||
.await?
|
.await?
|
||||||
@@ -323,16 +310,8 @@ async fn login_handler(
|
|||||||
let auth = auth_session::Entity::create(&state.db, user.id).await?;
|
let auth = auth_session::Entity::create(&state.db, user.id).await?;
|
||||||
let token = auth.value;
|
let token = auth.value;
|
||||||
|
|
||||||
let mut meow = Response::new("".to_string());
|
let jar = jar.add(Cookie::new("session", token));
|
||||||
// TODO: Replace with axum cookie jar https://docs.rs/axum-extra/latest/axum_extra/extract/cookie/struct.CookieJar.html
|
Ok((StatusCode::OK, jar))
|
||||||
meow.headers_mut().insert(
|
|
||||||
SET_COOKIE,
|
|
||||||
format!("session={token}; SameSite=Strict; Path=/; Max-Age=2592000")
|
|
||||||
.parse()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
*meow.status_mut() = StatusCode::OK;
|
|
||||||
Ok(meow)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -345,7 +324,8 @@ struct CreateUserForm {
|
|||||||
async fn create_user_handler(
|
async fn create_user_handler(
|
||||||
State(state): State<Arc<HttpServer>>,
|
State(state): State<Arc<HttpServer>>,
|
||||||
Json(payload): Json<CreateUserForm>,
|
Json(payload): Json<CreateUserForm>,
|
||||||
) -> Result<(HeaderMap, StatusCode), HttpError> {
|
jar: CookieJar,
|
||||||
|
) -> Result<impl IntoResponse, HttpError> {
|
||||||
if state.config.signup_code.is_empty() || payload.ref_token != state.config.signup_code {
|
if state.config.signup_code.is_empty() || payload.ref_token != state.config.signup_code {
|
||||||
warn!(username = %payload.username, "signup rejected: invalid signup code");
|
warn!(username = %payload.username, "signup rejected: invalid signup code");
|
||||||
return Err(HttpError::Unauthorized);
|
return Err(HttpError::Unauthorized);
|
||||||
@@ -372,15 +352,8 @@ async fn create_user_handler(
|
|||||||
let session = auth_session::Entity::create(&state.db, user.id).await?;
|
let session = auth_session::Entity::create(&state.db, user.id).await?;
|
||||||
let token = session.value;
|
let token = session.value;
|
||||||
|
|
||||||
let mut headers = HeaderMap::new();
|
let jar = jar.add(Cookie::new("session", token));
|
||||||
// TODO: Replace with axum cookie jar https://docs.rs/axum-extra/latest/axum_extra/extract/cookie/struct.CookieJar.html
|
Ok((jar, StatusCode::OK))
|
||||||
headers.insert(
|
|
||||||
SET_COOKIE,
|
|
||||||
format!("session={token}; HttpOnly; SameSite=Strict; Path=/")
|
|
||||||
.parse()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
Ok((headers, StatusCode::OK))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn stream_handler(
|
async fn stream_handler(
|
||||||
|
|||||||
Reference in New Issue
Block a user