From bfcebed7833e2fd6309f402c3a57b04484f651b2 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Wed, 30 Jul 2025 15:32:13 -0400 Subject: [PATCH 01/19] Added jwt crate --- Cargo.lock | 28 +++++++++++++++++++++++++--- Cargo.toml | 1 + 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0ede540..78f0e5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.22.1" @@ -770,6 +776,7 @@ dependencies = [ "icarus_envy", "icarus_meta", "icarus_models", + "jwt", "mime_guess", "serde", "serde_json", @@ -947,6 +954,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jwt" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" +dependencies = [ + "base64 0.13.1", + "crypto-common", + "digest", + "hmac", + "serde", + "serde_json", + "sha2", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1728,7 +1750,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "crc", "crossbeam-queue", @@ -1804,7 +1826,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags", "byteorder", "bytes", @@ -1848,7 +1870,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags", "byteorder", "crc", diff --git a/Cargo.toml b/Cargo.toml index a8715fb..28ac6b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ mime_guess = { version = "2.0.5" } uuid = { version = "1.17.0", features = ["v4", "serde"] } sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio-native-tls", "time", "uuid"] } time = { version = "0.3.41", features = ["formatting", "macros", "parsing", "serde"] } +jwt = { version = "0.16.0" } icarus_meta = { git = "ssh://git@git.kundeng.us/phoenix/icarus_meta.git", tag = "v0.3.0-devel-f4b71de969-680" } icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.5.1-devel-1c5de9dc26-111" } icarus_envy = { git = "ssh://git@git.kundeng.us/phoenix/icarus_envy.git", tag = "v0.3.0-devel-d73fba9899-006" } -- 2.47.3 From 83e9ec27921c31b97cc8fce4422da5f2572bde9b Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Wed, 30 Jul 2025 15:55:34 -0400 Subject: [PATCH 02/19] Saving changes Might go a different route later --- src/main.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/main.rs b/src/main.rs index 85719dd..a742d1b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -156,6 +156,77 @@ pub mod init { } } +mod auth { + use std::collections::BTreeMap; + + use serde::{Deserialize, Serialize}; + + use axum::{ + http::{Request, StatusCode}, + middleware::Next, + response::{IntoResponse, Response}, + Json, + }; + use jwt::{Algorithm, VerifyWithKey}; + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct UserClaims { + pub sub: String, // Subject (user ID) + pub exp: usize, // Expiration time + // Add any additional claims you need + } + + + pub async fn auth_middleware( + req: axum::http::Request, + next: axum::middleware::Next, + ) -> Result { + // Extract token from header + let token = extract_token_from_header(req.headers()) + .ok_or_else(|| (StatusCode::UNAUTHORIZED, "Missing token").into_response())?; + + // Create HMAC key from your secret + let key = jwt::Hmac::new("your-secret-key".as_bytes()) + .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Key error").into_response())?; + + // Verify and decode token + let claims: BTreeMap = token + .verify_with_key(&key) + .map_err(|_| (StatusCode::UNAUTHORIZED, "Invalid token").into_response())?; + + // Convert to your claims struct + let user_claims = serde_json::from_value::(serde_json::to_value(claims).unwrap()) + .map_err(|_| (StatusCode::UNAUTHORIZED, "Invalid claims").into_response())?; + + // Check expiration + if user_claims.exp < current_timestamp() { + return Err((StatusCode::UNAUTHORIZED, "Token expired").into_response()); + } + + // Attach claims to request + req.extensions_mut().insert(user_claims); + + Ok(next.run(req).await) + } + + // Helper to extract token from Authorization header + fn extract_token_from_header(headers: &HeaderMap) -> Option { + headers + .get("Authorization") + .and_then(|value| value.to_str().ok()) + .and_then(|value| value.strip_prefix("Bearer ")) + .map(|s| s.to_string()) + } + + // Helper to get current timestamp + fn current_timestamp() -> usize { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as usize + } +} + // TODO: Move elsewhere fn get_full() -> String { get_address() + ":" + &get_port() -- 2.47.3 From db93a4f138b1bc833367552cb4398720f5a3301a Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Wed, 30 Jul 2025 16:17:33 -0400 Subject: [PATCH 03/19] Crate changes --- Cargo.lock | 92 +++++++++++++++++++++++++++++++++++++++--------------- Cargo.toml | 3 +- 2 files changed, 69 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78f0e5f..724e777 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,12 +17,27 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + [[package]] name = "atoi" version = "2.0.0" @@ -108,12 +123,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.22.1" @@ -776,12 +785,13 @@ dependencies = [ "icarus_envy", "icarus_meta", "icarus_models", - "jwt", + "josekit", "mime_guess", "serde", "serde_json", "sqlx", "tempfile", + "thiserror 1.0.69", "time", "tokio", "tokio-util", @@ -944,6 +954,23 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "josekit" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a808e078330e6af222eb0044b71d4b1ff981bfef43e7bc8133a88234e0c86a0c" +dependencies = [ + "anyhow", + "base64", + "flate2", + "openssl", + "regex", + "serde", + "serde_json", + "thiserror 2.0.12", + "time", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -954,21 +981,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "jwt" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" -dependencies = [ - "base64 0.13.1", - "crypto-common", - "digest", - "hmac", - "serde", - "serde_json", - "sha2", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1488,6 +1500,35 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "rsa" version = "0.9.8" @@ -1603,6 +1644,7 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ + "indexmap", "itoa", "memchr", "ryu", @@ -1750,7 +1792,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" dependencies = [ - "base64 0.22.1", + "base64", "bytes", "crc", "crossbeam-queue", @@ -1826,7 +1868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", - "base64 0.22.1", + "base64", "bitflags", "byteorder", "bytes", @@ -1870,7 +1912,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", - "base64 0.22.1", + "base64", "bitflags", "byteorder", "crc", diff --git a/Cargo.toml b/Cargo.toml index 28ac6b1..f1e4cc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,8 @@ mime_guess = { version = "2.0.5" } uuid = { version = "1.17.0", features = ["v4", "serde"] } sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio-native-tls", "time", "uuid"] } time = { version = "0.3.41", features = ["formatting", "macros", "parsing", "serde"] } -jwt = { version = "0.16.0" } +thiserror = "1.0" +josekit = { version = "0.10.1" } icarus_meta = { git = "ssh://git@git.kundeng.us/phoenix/icarus_meta.git", tag = "v0.3.0-devel-f4b71de969-680" } icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.5.1-devel-1c5de9dc26-111" } icarus_envy = { git = "ssh://git@git.kundeng.us/phoenix/icarus_envy.git", tag = "v0.3.0-devel-d73fba9899-006" } -- 2.47.3 From 5fd4f120f300d5fd6e059ec16548e4bc3ab0cb26 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Wed, 30 Jul 2025 16:17:49 -0400 Subject: [PATCH 04/19] Adding auth module --- src/main.rs | 62 +++++++---------------------------------------------- 1 file changed, 8 insertions(+), 54 deletions(-) diff --git a/src/main.rs b/src/main.rs index a742d1b..daef760 100644 --- a/src/main.rs +++ b/src/main.rs @@ -167,64 +167,18 @@ mod auth { response::{IntoResponse, Response}, Json, }; - use jwt::{Algorithm, VerifyWithKey}; - #[derive(Clone, Debug, Serialize, Deserialize)] + use time::OffsetDateTime; + + #[derive(Debug, Serialize, Deserialize)] pub struct UserClaims { - pub sub: String, // Subject (user ID) - pub exp: usize, // Expiration time - // Add any additional claims you need + pub sub: String, // Subject (user ID) + pub exp: i64, // Expiration time (UTC timestamp) + pub iat: i64, // Issued at (UTC timestamp) + #[serde(skip_serializing_if = "Option::is_none")] + pub roles: Option>, // Optional roles } - - pub async fn auth_middleware( - req: axum::http::Request, - next: axum::middleware::Next, - ) -> Result { - // Extract token from header - let token = extract_token_from_header(req.headers()) - .ok_or_else(|| (StatusCode::UNAUTHORIZED, "Missing token").into_response())?; - - // Create HMAC key from your secret - let key = jwt::Hmac::new("your-secret-key".as_bytes()) - .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Key error").into_response())?; - - // Verify and decode token - let claims: BTreeMap = token - .verify_with_key(&key) - .map_err(|_| (StatusCode::UNAUTHORIZED, "Invalid token").into_response())?; - - // Convert to your claims struct - let user_claims = serde_json::from_value::(serde_json::to_value(claims).unwrap()) - .map_err(|_| (StatusCode::UNAUTHORIZED, "Invalid claims").into_response())?; - - // Check expiration - if user_claims.exp < current_timestamp() { - return Err((StatusCode::UNAUTHORIZED, "Token expired").into_response()); - } - - // Attach claims to request - req.extensions_mut().insert(user_claims); - - Ok(next.run(req).await) - } - - // Helper to extract token from Authorization header - fn extract_token_from_header(headers: &HeaderMap) -> Option { - headers - .get("Authorization") - .and_then(|value| value.to_str().ok()) - .and_then(|value| value.strip_prefix("Bearer ")) - .map(|s| s.to_string()) - } - - // Helper to get current timestamp - fn current_timestamp() -> usize { - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs() as usize - } } // TODO: Move elsewhere -- 2.47.3 From 7947bed5745be9991cc49252e0a68e5e4c5718ce Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Wed, 30 Jul 2025 17:25:22 -0400 Subject: [PATCH 05/19] Saving changes --- Cargo.lock | 15 +++++-- Cargo.toml | 1 + src/auth.rs | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 25 +----------- 4 files changed, 126 insertions(+), 28 deletions(-) create mode 100644 src/auth.rs diff --git a/Cargo.lock b/Cargo.lock index 724e777..bf228d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,6 +123,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -780,6 +786,7 @@ name = "icarus" version = "0.1.100" dependencies = [ "axum", + "base64 0.21.7", "common-multipart-rfc7578", "futures", "icarus_envy", @@ -961,7 +968,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a808e078330e6af222eb0044b71d4b1ff981bfef43e7bc8133a88234e0c86a0c" dependencies = [ "anyhow", - "base64", + "base64 0.22.1", "flate2", "openssl", "regex", @@ -1792,7 +1799,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "crc", "crossbeam-queue", @@ -1868,7 +1875,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags", "byteorder", "bytes", @@ -1912,7 +1919,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags", "byteorder", "crc", diff --git a/Cargo.toml b/Cargo.toml index f1e4cc2..9791cff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ uuid = { version = "1.17.0", features = ["v4", "serde"] } sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio-native-tls", "time", "uuid"] } time = { version = "0.3.41", features = ["formatting", "macros", "parsing", "serde"] } thiserror = "1.0" +base64 = "0.21" josekit = { version = "0.10.1" } icarus_meta = { git = "ssh://git@git.kundeng.us/phoenix/icarus_meta.git", tag = "v0.3.0-devel-f4b71de969-680" } icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.5.1-devel-1c5de9dc26-111" } diff --git a/src/auth.rs b/src/auth.rs new file mode 100644 index 0000000..18ae3ef --- /dev/null +++ b/src/auth.rs @@ -0,0 +1,113 @@ +use std::collections::BTreeMap; + + +use axum::{ + http::{Request, StatusCode}, + middleware::Next, + response::{IntoResponse, Response}, + Json, +}; +use josekit::{ + jwt::{self, JwtPayload}, + jws::{self, JwsHeader, JwsSigner, JwsVerifier}, + jwk::Jwk, +}; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use thiserror::Error; +use time::OffsetDateTime; + +#[derive(Debug, Serialize, Deserialize)] +pub struct UserClaims { + pub sub: String, // Subject (user ID) + pub exp: i64, // Expiration time (UTC timestamp) + pub iat: i64, // Issued at (UTC timestamp) + #[serde(skip_serializing_if = "Option::is_none")] + pub roles: Option>, // Optional roles +} + + +// use time::OffsetDateTime; + +#[derive(Error, Debug)] +pub enum JwtError { + #[error("Token creation failed")] + TokenCreation, + #[error("Token verification failed")] + TokenVerification, + #[error("Invalid token")] + InvalidToken, + #[error("Token expired")] + ExpiredToken, + #[error("Invalid key")] + InvalidKey, +} + +pub struct JwtAuth { + key: Jwk, // Now properly parameterized +} + +impl JwtAuth { + pub fn new(secret: &str) -> Result { + let mut key = Jwk::new("oct"); + key.set_parameter("k", Some(json!(base64::encode(secret)))) + .map_err(|_| JwtError::InvalidKey)?; + Ok(Self { key }) + } + + pub fn create_token(&self, claims: &UserClaims) -> Result { + let mut header = JwsHeader::new(); + header.set_token_type("JWT"); + + let claims_value = serde_json::to_value(claims) + .map_err(|_| JwtError::SerializationError)?; + let claims_map = claims_value.as_object() + .ok_or(JwtError::SerializationError)? + .to_owned(); + + let mut payload = JwtPayload::new(); + for (k, v) in claims_map { + payload.set_claim(&k, Some(v)) + .map_err(|_| JwtError::TokenCreation)?; + } + + let signer = jws::HS256.signer_from_jwk(&self.key) + .map_err(|_| JwtError::TokenCreation)?; + + jwt::encode_with_signer(&payload, &header, &signer) + .map_err(|_| JwtError::TokenCreation) + } + + pub fn verify_token(&self, token: &str) -> Result { + let verifier = jws::HS256.verifier_from_jwk(&self.key) + .map_err(|_| JwtError::TokenVerification)?; + + let (payload, _) = jwt::decode_with_verifier(token, &verifier) + .map_err(|_| JwtError::InvalidToken)?; + + // Convert payload to JSON value + let mut claims_value = json!({}); + if let Some(Value::Object(map)) = claims_value.as_object_mut() { + for key in payload.claim_names() { + if let Some(value) = payload.claim(key) { + map.insert(key.to_string(), value.clone()); + } + } + } + + let claims = serde_json::from_value(claims_value) + .map_err(|_| JwtError::InvalidToken)?; + + // Check expiration + if let Some(Value::Number(exp)) = payload.claim("exp") { + if let Some(exp) = exp.as_i64() { + let now = OffsetDateTime::now_utc().unix_timestamp(); + if exp < now { + return Err(JwtError::ExpiredToken); + } + } + } + + Ok(claims) + } +} diff --git a/src/main.rs b/src/main.rs index daef760..80e41f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +pub mod auth; pub mod callers; pub mod db { @@ -156,30 +157,6 @@ pub mod init { } } -mod auth { - use std::collections::BTreeMap; - - use serde::{Deserialize, Serialize}; - - use axum::{ - http::{Request, StatusCode}, - middleware::Next, - response::{IntoResponse, Response}, - Json, - }; - - use time::OffsetDateTime; - - #[derive(Debug, Serialize, Deserialize)] - pub struct UserClaims { - pub sub: String, // Subject (user ID) - pub exp: i64, // Expiration time (UTC timestamp) - pub iat: i64, // Issued at (UTC timestamp) - #[serde(skip_serializing_if = "Option::is_none")] - pub roles: Option>, // Optional roles - } - -} // TODO: Move elsewhere fn get_full() -> String { -- 2.47.3 From 9a60b845389bb6f73be23881cec52ed86bb96be4 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Wed, 30 Jul 2025 17:26:56 -0400 Subject: [PATCH 06/19] Added jsonwebtoken crate --- Cargo.lock | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + 2 files changed, 71 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index bf228d3..159cbfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -609,8 +609,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -793,6 +795,7 @@ dependencies = [ "icarus_meta", "icarus_models", "josekit", + "jsonwebtoken", "mime_guess", "serde", "serde_json", @@ -988,6 +991,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64 0.22.1", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1175,6 +1193,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -1337,6 +1365,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pem" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" +dependencies = [ + "base64 0.22.1", + "serde", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -1536,6 +1574,20 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rsa" version = "0.9.8" @@ -1736,6 +1788,18 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simple_asn1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.12", + "time", +] + [[package]] name = "slab" version = "0.4.10" @@ -2350,6 +2414,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.4" diff --git a/Cargo.toml b/Cargo.toml index 9791cff..cafbc2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio-native-tls", time = { version = "0.3.41", features = ["formatting", "macros", "parsing", "serde"] } thiserror = "1.0" base64 = "0.21" +jsonwebtoken = { version = "9.3.1" } josekit = { version = "0.10.1" } icarus_meta = { git = "ssh://git@git.kundeng.us/phoenix/icarus_meta.git", tag = "v0.3.0-devel-f4b71de969-680" } icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.5.1-devel-1c5de9dc26-111" } -- 2.47.3 From c8ffd8411df09d1a65b3a8a4d838d0191bb7267b Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Wed, 30 Jul 2025 19:08:44 -0400 Subject: [PATCH 07/19] Saving changes --- Cargo.lock | 35 +++++++++++++ Cargo.toml | 1 + src/auth.rs | 148 ++++++++++++++++++++++++++-------------------------- 3 files changed, 110 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 159cbfb..8c093dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,29 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-extra" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bf463831f5131b7d3c756525b305d40f1185b688565648a92e1392ca35713d" +dependencies = [ + "axum", + "axum-core", + "bytes", + "cookie", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "serde", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.75" @@ -243,6 +266,17 @@ dependencies = [ "unicode-xid", ] +[[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]] name = "core-foundation" version = "0.9.4" @@ -788,6 +822,7 @@ name = "icarus" version = "0.1.100" dependencies = [ "axum", + "axum-extra", "base64 0.21.7", "common-multipart-rfc7578", "futures", diff --git a/Cargo.toml b/Cargo.toml index cafbc2f..f9d5876 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ rust-version = "1.88" [dependencies] axum = { version = "0.8.4", features = ["multipart"] } +axum-extra = { version = "0.10.1", features = ["cookie"] } serde = { version = "1.0.219", features = ["derive"] } serde_json = { version = "1.0.140" } tower = { version = "0.5.2", features = ["full"] } diff --git a/src/auth.rs b/src/auth.rs index 18ae3ef..caa2c58 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,24 +1,25 @@ -use std::collections::BTreeMap; - +// use std::collections::BTreeMap; +// use std::sync::Arc; use axum::{ + // extract::State, http::{Request, StatusCode}, middleware::Next, - response::{IntoResponse, Response}, + response::{IntoResponse}, Json, }; -use josekit::{ - jwt::{self, JwtPayload}, - jws::{self, JwsHeader, JwsSigner, JwsVerifier}, - jwk::Jwk, -}; +use axum_extra::extract::cookie::CookieJar; +use jsonwebtoken::{decode, DecodingKey, Validation}; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; +// use serde_json::{json, Value}; use thiserror::Error; -use time::OffsetDateTime; +// use time::OffsetDateTime; + + #[derive(Debug, Serialize, Deserialize)] pub struct UserClaims { + pub aud: String, // Audience pub sub: String, // Subject (user ID) pub exp: i64, // Expiration time (UTC timestamp) pub iat: i64, // Issued at (UTC timestamp) @@ -27,8 +28,6 @@ pub struct UserClaims { } -// use time::OffsetDateTime; - #[derive(Error, Debug)] pub enum JwtError { #[error("Token creation failed")] @@ -43,71 +42,72 @@ pub enum JwtError { InvalidKey, } +/* pub struct JwtAuth { key: Jwk, // Now properly parameterized } +*/ -impl JwtAuth { - pub fn new(secret: &str) -> Result { - let mut key = Jwk::new("oct"); - key.set_parameter("k", Some(json!(base64::encode(secret)))) - .map_err(|_| JwtError::InvalidKey)?; - Ok(Self { key }) - } - pub fn create_token(&self, claims: &UserClaims) -> Result { - let mut header = JwsHeader::new(); - header.set_token_type("JWT"); - - let claims_value = serde_json::to_value(claims) - .map_err(|_| JwtError::SerializationError)?; - let claims_map = claims_value.as_object() - .ok_or(JwtError::SerializationError)? - .to_owned(); - - let mut payload = JwtPayload::new(); - for (k, v) in claims_map { - payload.set_claim(&k, Some(v)) - .map_err(|_| JwtError::TokenCreation)?; - } - - let signer = jws::HS256.signer_from_jwk(&self.key) - .map_err(|_| JwtError::TokenCreation)?; - - jwt::encode_with_signer(&payload, &header, &signer) - .map_err(|_| JwtError::TokenCreation) - } - - pub fn verify_token(&self, token: &str) -> Result { - let verifier = jws::HS256.verifier_from_jwk(&self.key) - .map_err(|_| JwtError::TokenVerification)?; - - let (payload, _) = jwt::decode_with_verifier(token, &verifier) - .map_err(|_| JwtError::InvalidToken)?; - - // Convert payload to JSON value - let mut claims_value = json!({}); - if let Some(Value::Object(map)) = claims_value.as_object_mut() { - for key in payload.claim_names() { - if let Some(value) = payload.claim(key) { - map.insert(key.to_string(), value.clone()); - } - } - } - - let claims = serde_json::from_value(claims_value) - .map_err(|_| JwtError::InvalidToken)?; - - // Check expiration - if let Some(Value::Number(exp)) = payload.claim("exp") { - if let Some(exp) = exp.as_i64() { - let now = OffsetDateTime::now_utc().unix_timestamp(); - if exp < now { - return Err(JwtError::ExpiredToken); - } - } - } - - Ok(claims) - } +#[derive(Debug, Serialize)] +pub struct ErrorResponse { + pub status: &'static str, + pub message: String, +} + +pub async fn auth( + cookie_jar: CookieJar, + // State(data): State>, + mut req: Request, + next: Next, +) -> Result)> { + let token = cookie_jar + .get("token") + .map(|cookie| cookie.value().to_string()) + .or_else(|| { + req.headers() + .get(axum::http::header::AUTHORIZATION) + .and_then(|auth_header| auth_header.to_str().ok()) + .and_then(|auth_value| { + if auth_value.starts_with("Bearer ") { + Some(auth_value[7..].to_owned()) + } else { + None + } + }) + }); + + let token = token.ok_or_else(|| { + let json_error = ErrorResponse { + status: "fail", + message: "You are not logged in, please provide token".to_string(), + }; + (StatusCode::UNAUTHORIZED, Json(json_error)) + })?; + + let claims = decode::( + &token, + // TODO: Replace with code to get secret from env + &DecodingKey::from_secret("my_super_secret".as_ref()), + &Validation::default(), + ) + .map_err(|_| { + let json_error = ErrorResponse { + status: "fail", + message: "Invalid token".to_string(), + }; + (StatusCode::UNAUTHORIZED, Json(json_error)) + })? + .claims; + + let user_id = uuid::Uuid::parse_str(&claims.sub).map_err(|_| { + let json_error = ErrorResponse { + status: "fail", + message: "Invalid token".to_string(), + }; + (StatusCode::UNAUTHORIZED, Json(json_error)) + })?; + + req.extensions_mut().insert(user_id); + Ok(next.run(req).await) } -- 2.47.3 From d969c31d02db44b14fa2bcc76e833b1738425755 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Wed, 30 Jul 2025 19:19:23 -0400 Subject: [PATCH 08/19] Added feature to tower-http crate --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f9d5876..a6e932f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ serde_json = { version = "1.0.140" } tower = { version = "0.5.2", features = ["full"] } tokio = { version = "1.45.1", features = ["full"] } tokio-util = { version = "0.7.15", features = ["io"] } -tower-http = { version = "0.6.6", features = ["timeout"] } +tower-http = { version = "0.6.6", features = ["cors", "timeout"] } tracing-subscriber = "0.3.19" futures = { version = "0.3.31" } mime_guess = { version = "2.0.5" } -- 2.47.3 From cdedeccec3821a584233a8b700c379601439c339 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Wed, 30 Jul 2025 19:23:04 -0400 Subject: [PATCH 09/19] Using secret key from env file --- src/auth.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/auth.rs b/src/auth.rs index caa2c58..08f4ae7 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -85,10 +85,12 @@ pub async fn auth( (StatusCode::UNAUTHORIZED, Json(json_error)) })?; + let secret_key = icarus_envy::environment::get_secret_main_key().await; + let claims = decode::( &token, // TODO: Replace with code to get secret from env - &DecodingKey::from_secret("my_super_secret".as_ref()), + &DecodingKey::from_secret(secret_key.as_ref()), &Validation::default(), ) .map_err(|_| { -- 2.47.3 From 1784852b1e1cb7c7d964f1470855392ae83a9ebe Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Wed, 30 Jul 2025 19:23:17 -0400 Subject: [PATCH 10/19] Locking down one endpoint to start --- src/main.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 80e41f0..762d232 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,12 +49,19 @@ pub mod init { use std::time::Duration; use tower_http::timeout::TimeoutLayer; + use axum::http::{ + header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE}, + HeaderValue, Method, + }; + pub async fn routes() -> axum::Router { axum::Router::new() .route(crate::ROOT, get(crate::root)) .route( crate::callers::endpoints::QUEUESONG, - post(crate::callers::song::endpoint::queue_song), + post(crate::callers::song::endpoint::queue_song) + // .route_layer(axum::middleware::from_fn_with_state(app_state.clone(), crate::auth::auth)), + // .route_layer(axum::middleware::from_fn(crate::auth::auth::)), ) .route( crate::callers::endpoints::QUEUESONG, @@ -149,11 +156,18 @@ pub mod init { // TODO: Look into handling this. Seems redundant to run migrations multiple times crate::db::migrations(&pool).await; + let cors = tower_http::cors::CorsLayer::new() + .allow_origin("http://localhost:3000".parse::().unwrap()) + .allow_methods([Method::GET, Method::POST, Method::PATCH, Method::DELETE]) + .allow_credentials(true) + .allow_headers([AUTHORIZATION, ACCEPT, CONTENT_TYPE]); + routes() .await .layer(axum::Extension(pool)) .layer(axum::extract::DefaultBodyLimit::max(1024 * 1024 * 1024)) .layer(TimeoutLayer::new(Duration::from_secs(300))) + .layer(cors) } } -- 2.47.3 From c9f6fab746106b4f76898d765061471e7f3127ef Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Wed, 30 Jul 2025 20:03:24 -0400 Subject: [PATCH 11/19] Warning fix --- src/auth.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auth.rs b/src/auth.rs index 08f4ae7..6774026 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -69,8 +69,8 @@ pub async fn auth( .get(axum::http::header::AUTHORIZATION) .and_then(|auth_header| auth_header.to_str().ok()) .and_then(|auth_value| { - if auth_value.starts_with("Bearer ") { - Some(auth_value[7..].to_owned()) + if let Some(stripped) = auth_value.strip_prefix("Bearer ") { + Some(String::from(stripped)) } else { None } -- 2.47.3 From 7ecb4d5c4bc904c62cf6a4f12c368bb19760d20f Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Wed, 30 Jul 2025 20:46:55 -0400 Subject: [PATCH 12/19] Got auth functioning --- src/auth.rs | 40 ++++++++++++++++++++++++++++++++++++---- src/main.rs | 2 +- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/auth.rs b/src/auth.rs index 6774026..e25b5b2 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -16,13 +16,35 @@ use thiserror::Error; // use time::OffsetDateTime; +fn deserialize_i64_from_f64<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let val = f64::deserialize(deserializer)?; + // Handle NaN and infinity cases + if val.is_nan() || val.is_infinite() { + return Err(serde::de::Error::custom("invalid float value")); + } + // Round to nearest integer and convert + let rounded = val.round(); + // Check if the rounded value can fit in i64 + if rounded < (i64::MIN as f64) || rounded > (i64::MAX as f64) { + return Err(serde::de::Error::custom("float out of i64 range")); + } + Ok(rounded as i64) +} #[derive(Debug, Serialize, Deserialize)] pub struct UserClaims { + pub iss: String, pub aud: String, // Audience pub sub: String, // Subject (user ID) + #[serde(deserialize_with = "deserialize_i64_from_f64")] pub exp: i64, // Expiration time (UTC timestamp) + #[serde(deserialize_with = "deserialize_i64_from_f64")] pub iat: i64, // Issued at (UTC timestamp) + // pub azp: String, + // pub gty: String, #[serde(skip_serializing_if = "Option::is_none")] pub roles: Option>, // Optional roles } @@ -61,6 +83,8 @@ pub async fn auth( mut req: Request, next: Next, ) -> Result)> { + println!("Cookie: {cookie_jar:?}"); + let token = cookie_jar .get("token") .map(|cookie| cookie.value().to_string()) @@ -69,6 +93,7 @@ pub async fn auth( .get(axum::http::header::AUTHORIZATION) .and_then(|auth_header| auth_header.to_str().ok()) .and_then(|auth_value| { + println!("Auth value: {auth_value:?}"); if let Some(stripped) = auth_value.strip_prefix("Bearer ") { Some(String::from(stripped)) } else { @@ -87,21 +112,27 @@ pub async fn auth( let secret_key = icarus_envy::environment::get_secret_main_key().await; + let mut validation = Validation::new(jsonwebtoken::Algorithm::HS256); + validation.set_audience(&["icarus"]); // Must match exactly what's in the token let claims = decode::( &token, // TODO: Replace with code to get secret from env &DecodingKey::from_secret(secret_key.as_ref()), - &Validation::default(), + &validation, ) - .map_err(|_| { + .map_err(|err| { + eprintln!("Error: {err:?}"); let json_error = ErrorResponse { status: "fail", - message: "Invalid token".to_string(), + message: "Invalid token - Error decoding claims".to_string(), }; (StatusCode::UNAUTHORIZED, Json(json_error)) })? .claims; + println!("Claims: {claims:?}"); + + /* let user_id = uuid::Uuid::parse_str(&claims.sub).map_err(|_| { let json_error = ErrorResponse { status: "fail", @@ -109,7 +140,8 @@ pub async fn auth( }; (StatusCode::UNAUTHORIZED, Json(json_error)) })?; + */ - req.extensions_mut().insert(user_id); + // req.extensions_mut().insert(user_id); Ok(next.run(req).await) } diff --git a/src/main.rs b/src/main.rs index 762d232..b80007a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,7 +61,7 @@ pub mod init { crate::callers::endpoints::QUEUESONG, post(crate::callers::song::endpoint::queue_song) // .route_layer(axum::middleware::from_fn_with_state(app_state.clone(), crate::auth::auth)), - // .route_layer(axum::middleware::from_fn(crate::auth::auth::)), + .route_layer(axum::middleware::from_fn(crate::auth::auth::)), ) .route( crate::callers::endpoints::QUEUESONG, -- 2.47.3 From c44658d0b8789e513d5e6b34b6df6179b56476cc Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Thu, 31 Jul 2025 20:55:51 -0400 Subject: [PATCH 13/19] Updated icarus_models --- Cargo.lock | 5 +++-- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c093dd..2afa4fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -866,9 +866,10 @@ dependencies = [ [[package]] name = "icarus_models" -version = "0.5.1" -source = "git+ssh://git@git.kundeng.us/phoenix/icarus_models.git?tag=v0.5.1-devel-1c5de9dc26-111#1c5de9dc26099ac5f21e1dc7a1f7d0dbb892b34b" +version = "0.5.2" +source = "git+ssh://git@git.kundeng.us/phoenix/icarus_models.git?tag=v0.5.2-devel-d3251f935e-111#d3251f935e76caa110727203d910ee0683a668a0" dependencies = [ + "josekit", "rand 0.9.1", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index a6e932f..92ec979 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ base64 = "0.21" jsonwebtoken = { version = "9.3.1" } josekit = { version = "0.10.1" } icarus_meta = { git = "ssh://git@git.kundeng.us/phoenix/icarus_meta.git", tag = "v0.3.0-devel-f4b71de969-680" } -icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.5.1-devel-1c5de9dc26-111" } +icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.5.2-devel-d3251f935e-111" } icarus_envy = { git = "ssh://git@git.kundeng.us/phoenix/icarus_envy.git", tag = "v0.3.0-devel-d73fba9899-006" } [dev-dependencies] -- 2.47.3 From 83a1c9ce4766a95c9584821940b3833ca9aa9796 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Thu, 31 Jul 2025 23:04:27 -0400 Subject: [PATCH 14/19] Fixed auth in tests --- src/main.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/main.rs b/src/main.rs index b80007a..a06dc19 100644 --- a/src/main.rs +++ b/src/main.rs @@ -291,6 +291,38 @@ mod tests { } } + pub fn token_fields() -> (String, String, String) { + (String::from("What a twist!"), String::from("icarus_test"), String::from("icarus")) + } + + pub async fn test_token() -> Result { + let key: String = icarus_envy::environment::get_secret_main_key().await; + let (message, issuer, audience) = token_fields(); + + match icarus_models::token::create_token(&key, &message, &issuer, &audience) { + Ok((access_token, _some_time)) => { + Ok(access_token) + } + Err(err) => { + Err(err) + } + } + } + + pub async fn bearer_auth() -> String { + let token = match test_token().await { + Ok(access_token) => { + access_token + } + Err(err) => { + assert!(false, "Error: {err:?}"); + String::new() + } + }; + + format!("Bearer {token}") + } + // TODO: Put the *_req() functions in their own module async fn song_queue_req( app: &axum::Router, @@ -299,6 +331,7 @@ mod tests { let mut form = MultipartForm::default(); let _ = form.add_file("flac", "tests/I/track01.flac"); + // Create request let content_type = form.content_type(); let body = MultipartBody::from(form); @@ -306,6 +339,7 @@ mod tests { .method(axum::http::Method::POST) .uri(crate::callers::endpoints::QUEUESONG) .header(axum::http::header::CONTENT_TYPE, content_type) + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from_stream(body)) .unwrap(); app.clone().oneshot(req).await @@ -325,6 +359,7 @@ mod tests { .method(axum::http::Method::PATCH) .uri(crate::callers::endpoints::QUEUESONGLINKUSERID) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(); @@ -338,6 +373,7 @@ mod tests { .method(axum::http::Method::GET) .uri(crate::callers::endpoints::NEXTQUEUESONG) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(); app.clone().oneshot(fetch_req).await @@ -353,6 +389,7 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(); @@ -371,6 +408,7 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "audio/flac") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(); @@ -391,6 +429,7 @@ mod tests { .method(axum::http::Method::POST) .uri(crate::callers::endpoints::QUEUECOVERART) .header(axum::http::header::CONTENT_TYPE, content_type) + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from_stream(body)) .unwrap(); @@ -408,6 +447,7 @@ mod tests { .method(axum::http::Method::POST) .uri(crate::callers::endpoints::QUEUEMETADATA) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(); @@ -428,6 +468,7 @@ mod tests { .method(axum::http::Method::PATCH) .uri(crate::callers::endpoints::QUEUECOVERARTLINK) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(); @@ -447,6 +488,7 @@ mod tests { .method(axum::http::Method::POST) .uri(crate::callers::endpoints::CREATECOVERART) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(); app.clone().oneshot(req).await @@ -463,6 +505,7 @@ mod tests { .method(axum::http::Method::POST) .uri(crate::callers::endpoints::CREATESONG) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(); @@ -482,6 +525,7 @@ mod tests { .method(axum::http::Method::PATCH) .uri(crate::callers::endpoints::QUEUESONG) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(); @@ -502,6 +546,7 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(); @@ -966,6 +1011,7 @@ mod tests { .method(axum::http::Method::PATCH) .uri(uri) .header(axum::http::header::CONTENT_TYPE, content_type) + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from_stream(body)) .unwrap(), ) @@ -1428,6 +1474,7 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "image/jpeg") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(), ) @@ -1696,6 +1743,7 @@ mod tests { .method(axum::http::Method::PATCH) .uri(crate::callers::endpoints::QUEUESONGDATAWIPE) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(), ) @@ -1816,6 +1864,7 @@ mod tests { axum::http::header::CONTENT_TYPE, "application/json", ) + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(), ) @@ -1889,6 +1938,7 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, super::bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(), ) @@ -1942,6 +1992,7 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, super::bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(), ) @@ -1994,6 +2045,7 @@ mod tests { pub async fn coverart_id() -> Result { uuid::Uuid::parse_str("996122cd-5ae9-4013-9934-60768d3006ed") } + } #[tokio::test] @@ -2027,6 +2079,7 @@ mod tests { axum::http::Request::builder() .method(axum::http::Method::GET) .uri(&uri) + .header(axum::http::header::AUTHORIZATION, super::bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(), ) @@ -2082,6 +2135,7 @@ mod tests { axum::http::Request::builder() .method(axum::http::Method::GET) .uri(&uri) + .header(axum::http::header::AUTHORIZATION, super::bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(), ) @@ -2138,6 +2192,7 @@ mod tests { axum::http::Request::builder() .method(axum::http::Method::GET) .uri(&uri) + .header(axum::http::header::AUTHORIZATION, super::bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(), ) @@ -2273,6 +2328,7 @@ mod tests { axum::http::Request::builder() .method(axum::http::Method::DELETE) .uri(&uri) + .header(axum::http::header::AUTHORIZATION, super::bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(), ) -- 2.47.3 From 78be4363edaad270499e4bef837681eda1d9e1a9 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Thu, 31 Jul 2025 23:08:09 -0400 Subject: [PATCH 15/19] Warning fixes --- src/auth.rs | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/src/auth.rs b/src/auth.rs index e25b5b2..104182a 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -2,7 +2,6 @@ // use std::sync::Arc; use axum::{ - // extract::State, http::{Request, StatusCode}, middleware::Next, response::{IntoResponse}, @@ -64,12 +63,6 @@ pub enum JwtError { InvalidKey, } -/* -pub struct JwtAuth { - key: Jwk, // Now properly parameterized -} -*/ - #[derive(Debug, Serialize)] pub struct ErrorResponse { @@ -79,8 +72,7 @@ pub struct ErrorResponse { pub async fn auth( cookie_jar: CookieJar, - // State(data): State>, - mut req: Request, + req: Request, next: Next, ) -> Result)> { println!("Cookie: {cookie_jar:?}"); @@ -94,11 +86,7 @@ pub async fn auth( .and_then(|auth_header| auth_header.to_str().ok()) .and_then(|auth_value| { println!("Auth value: {auth_value:?}"); - if let Some(stripped) = auth_value.strip_prefix("Bearer ") { - Some(String::from(stripped)) - } else { - None - } + auth_value.strip_prefix("Bearer ").map(String::from) }) }); @@ -132,16 +120,5 @@ pub async fn auth( println!("Claims: {claims:?}"); - /* - let user_id = uuid::Uuid::parse_str(&claims.sub).map_err(|_| { - let json_error = ErrorResponse { - status: "fail", - message: "Invalid token".to_string(), - }; - (StatusCode::UNAUTHORIZED, Json(json_error)) - })?; - */ - - // req.extensions_mut().insert(user_id); Ok(next.run(req).await) } -- 2.47.3 From 30522b3a495bb43f025541d551161f58e343edf8 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Thu, 31 Jul 2025 23:08:23 -0400 Subject: [PATCH 16/19] Code formatting --- src/auth.rs | 19 +++++------- src/main.rs | 84 ++++++++++++++++++++++++++++++++++------------------- 2 files changed, 62 insertions(+), 41 deletions(-) diff --git a/src/auth.rs b/src/auth.rs index 104182a..f3bf405 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -2,19 +2,18 @@ // use std::sync::Arc; use axum::{ + Json, http::{Request, StatusCode}, middleware::Next, - response::{IntoResponse}, - Json, + response::IntoResponse, }; use axum_extra::extract::cookie::CookieJar; -use jsonwebtoken::{decode, DecodingKey, Validation}; +use jsonwebtoken::{DecodingKey, Validation, decode}; use serde::{Deserialize, Serialize}; // use serde_json::{json, Value}; use thiserror::Error; // use time::OffsetDateTime; - fn deserialize_i64_from_f64<'de, D>(deserializer: D) -> Result where D: serde::Deserializer<'de>, @@ -36,19 +35,18 @@ where #[derive(Debug, Serialize, Deserialize)] pub struct UserClaims { pub iss: String, - pub aud: String, // Audience - pub sub: String, // Subject (user ID) + pub aud: String, // Audience + pub sub: String, // Subject (user ID) #[serde(deserialize_with = "deserialize_i64_from_f64")] - pub exp: i64, // Expiration time (UTC timestamp) + pub exp: i64, // Expiration time (UTC timestamp) #[serde(deserialize_with = "deserialize_i64_from_f64")] - pub iat: i64, // Issued at (UTC timestamp) + pub iat: i64, // Issued at (UTC timestamp) // pub azp: String, // pub gty: String, #[serde(skip_serializing_if = "Option::is_none")] - pub roles: Option>, // Optional roles + pub roles: Option>, // Optional roles } - #[derive(Error, Debug)] pub enum JwtError { #[error("Token creation failed")] @@ -63,7 +61,6 @@ pub enum JwtError { InvalidKey, } - #[derive(Debug, Serialize)] pub struct ErrorResponse { pub status: &'static str, diff --git a/src/main.rs b/src/main.rs index a06dc19..1d08116 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,8 +50,8 @@ pub mod init { use tower_http::timeout::TimeoutLayer; use axum::http::{ - header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE}, HeaderValue, Method, + header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE}, }; pub async fn routes() -> axum::Router { @@ -60,8 +60,10 @@ pub mod init { .route( crate::callers::endpoints::QUEUESONG, post(crate::callers::song::endpoint::queue_song) - // .route_layer(axum::middleware::from_fn_with_state(app_state.clone(), crate::auth::auth)), - .route_layer(axum::middleware::from_fn(crate::auth::auth::)), + // .route_layer(axum::middleware::from_fn_with_state(app_state.clone(), crate::auth::auth)), + .route_layer(axum::middleware::from_fn( + crate::auth::auth::, + )), ) .route( crate::callers::endpoints::QUEUESONG, @@ -157,10 +159,10 @@ pub mod init { crate::db::migrations(&pool).await; let cors = tower_http::cors::CorsLayer::new() - .allow_origin("http://localhost:3000".parse::().unwrap()) - .allow_methods([Method::GET, Method::POST, Method::PATCH, Method::DELETE]) - .allow_credentials(true) - .allow_headers([AUTHORIZATION, ACCEPT, CONTENT_TYPE]); + .allow_origin("http://localhost:3000".parse::().unwrap()) + .allow_methods([Method::GET, Method::POST, Method::PATCH, Method::DELETE]) + .allow_credentials(true) + .allow_headers([AUTHORIZATION, ACCEPT, CONTENT_TYPE]); routes() .await @@ -171,7 +173,6 @@ pub mod init { } } - // TODO: Move elsewhere fn get_full() -> String { get_address() + ":" + &get_port() @@ -292,7 +293,11 @@ mod tests { } pub fn token_fields() -> (String, String, String) { - (String::from("What a twist!"), String::from("icarus_test"), String::from("icarus")) + ( + String::from("What a twist!"), + String::from("icarus_test"), + String::from("icarus"), + ) } pub async fn test_token() -> Result { @@ -300,20 +305,14 @@ mod tests { let (message, issuer, audience) = token_fields(); match icarus_models::token::create_token(&key, &message, &issuer, &audience) { - Ok((access_token, _some_time)) => { - Ok(access_token) - } - Err(err) => { - Err(err) - } + Ok((access_token, _some_time)) => Ok(access_token), + Err(err) => Err(err), } } pub async fn bearer_auth() -> String { - let token = match test_token().await { - Ok(access_token) => { - access_token - } + let token = match test_token().await { + Ok(access_token) => access_token, Err(err) => { assert!(false, "Error: {err:?}"); String::new() @@ -331,7 +330,6 @@ mod tests { let mut form = MultipartForm::default(); let _ = form.add_file("flac", "tests/I/track01.flac"); - // Create request let content_type = form.content_type(); let body = MultipartBody::from(form); @@ -1011,7 +1009,10 @@ mod tests { .method(axum::http::Method::PATCH) .uri(uri) .header(axum::http::header::CONTENT_TYPE, content_type) - .header(axum::http::header::AUTHORIZATION, bearer_auth().await) + .header( + axum::http::header::AUTHORIZATION, + bearer_auth().await, + ) .body(axum::body::Body::from_stream(body)) .unwrap(), ) @@ -1474,7 +1475,10 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "image/jpeg") - .header(axum::http::header::AUTHORIZATION, bearer_auth().await) + .header( + axum::http::header::AUTHORIZATION, + bearer_auth().await, + ) .body(axum::body::Body::empty()) .unwrap(), ) @@ -1864,7 +1868,10 @@ mod tests { axum::http::header::CONTENT_TYPE, "application/json", ) - .header(axum::http::header::AUTHORIZATION, bearer_auth().await) + .header( + axum::http::header::AUTHORIZATION, + bearer_auth().await, + ) .body(axum::body::Body::from(payload.to_string())) .unwrap(), ) @@ -1938,7 +1945,10 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "application/json") - .header(axum::http::header::AUTHORIZATION, super::bearer_auth().await) + .header( + axum::http::header::AUTHORIZATION, + super::bearer_auth().await, + ) .body(axum::body::Body::empty()) .unwrap(), ) @@ -1992,7 +2002,10 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "application/json") - .header(axum::http::header::AUTHORIZATION, super::bearer_auth().await) + .header( + axum::http::header::AUTHORIZATION, + super::bearer_auth().await, + ) .body(axum::body::Body::empty()) .unwrap(), ) @@ -2045,7 +2058,6 @@ mod tests { pub async fn coverart_id() -> Result { uuid::Uuid::parse_str("996122cd-5ae9-4013-9934-60768d3006ed") } - } #[tokio::test] @@ -2079,7 +2091,10 @@ mod tests { axum::http::Request::builder() .method(axum::http::Method::GET) .uri(&uri) - .header(axum::http::header::AUTHORIZATION, super::bearer_auth().await) + .header( + axum::http::header::AUTHORIZATION, + super::bearer_auth().await, + ) .body(axum::body::Body::empty()) .unwrap(), ) @@ -2135,7 +2150,10 @@ mod tests { axum::http::Request::builder() .method(axum::http::Method::GET) .uri(&uri) - .header(axum::http::header::AUTHORIZATION, super::bearer_auth().await) + .header( + axum::http::header::AUTHORIZATION, + super::bearer_auth().await, + ) .body(axum::body::Body::empty()) .unwrap(), ) @@ -2192,7 +2210,10 @@ mod tests { axum::http::Request::builder() .method(axum::http::Method::GET) .uri(&uri) - .header(axum::http::header::AUTHORIZATION, super::bearer_auth().await) + .header( + axum::http::header::AUTHORIZATION, + super::bearer_auth().await, + ) .body(axum::body::Body::empty()) .unwrap(), ) @@ -2328,7 +2349,10 @@ mod tests { axum::http::Request::builder() .method(axum::http::Method::DELETE) .uri(&uri) - .header(axum::http::header::AUTHORIZATION, super::bearer_auth().await) + .header( + axum::http::header::AUTHORIZATION, + super::bearer_auth().await, + ) .body(axum::body::Body::empty()) .unwrap(), ) -- 2.47.3 From c9d3434df703bc46f662060eee08bfae79598155 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Thu, 31 Jul 2025 23:16:24 -0400 Subject: [PATCH 17/19] Added auth to the other endpoints --- src/main.rs | 165 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 103 insertions(+), 62 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1d08116..8207964 100644 --- a/src/main.rs +++ b/src/main.rs @@ -59,95 +59,136 @@ pub mod init { .route(crate::ROOT, get(crate::root)) .route( crate::callers::endpoints::QUEUESONG, - post(crate::callers::song::endpoint::queue_song) - // .route_layer(axum::middleware::from_fn_with_state(app_state.clone(), crate::auth::auth)), + post(crate::callers::song::endpoint::queue_song).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) + .route( + crate::callers::endpoints::QUEUESONG, + patch(crate::callers::song::endpoint::update_song_queue_status).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) + .route( + crate::callers::endpoints::QUEUESONGLINKUSERID, + patch(crate::callers::song::endpoint::link_user_id).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) + .route( + crate::callers::endpoints::QUEUESONGDATA, + get(crate::callers::song::endpoint::download_flac).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) + .route( + crate::callers::endpoints::NEXTQUEUESONG, + get(crate::callers::song::endpoint::fetch_queue_song).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) + .route( + crate::callers::endpoints::QUEUESONGUPDATE, + patch(crate::callers::song::endpoint::update_song_queue).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) + .route( + crate::callers::endpoints::QUEUESONGDATAWIPE, + patch(crate::callers::song::endpoint::wipe_data_from_song_queue).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) + .route( + crate::callers::endpoints::QUEUEMETADATA, + post(crate::callers::metadata::endpoint::queue_metadata).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) + .route( + crate::callers::endpoints::QUEUEMETADATA, + get(crate::callers::metadata::endpoint::fetch_metadata).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) + .route( + crate::callers::endpoints::QUEUECOVERART, + post(crate::callers::coverart::endpoint::queue).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) + .route( + crate::callers::endpoints::QUEUECOVERARTDATA, + get(crate::callers::coverart::endpoint::fetch_coverart_with_data).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) + .route( + crate::callers::endpoints::QUEUECOVERART, + get(crate::callers::coverart::endpoint::fetch_coverart_no_data).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) + .route( + crate::callers::endpoints::QUEUECOVERARTLINK, + patch(crate::callers::coverart::endpoint::link).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), + ) + .route( + crate::callers::endpoints::QUEUECOVERARTDATAWIPE, + patch(crate::callers::coverart::endpoint::wipe_data_from_coverart_queue) .route_layer(axum::middleware::from_fn( crate::auth::auth::, )), ) - .route( - crate::callers::endpoints::QUEUESONG, - patch(crate::callers::song::endpoint::update_song_queue_status), - ) - .route( - crate::callers::endpoints::QUEUESONGLINKUSERID, - patch(crate::callers::song::endpoint::link_user_id), - ) - .route( - crate::callers::endpoints::QUEUESONGDATA, - get(crate::callers::song::endpoint::download_flac), - ) - .route( - crate::callers::endpoints::NEXTQUEUESONG, - get(crate::callers::song::endpoint::fetch_queue_song), - ) - .route( - crate::callers::endpoints::QUEUESONGUPDATE, - patch(crate::callers::song::endpoint::update_song_queue), - ) - .route( - crate::callers::endpoints::QUEUESONGDATAWIPE, - patch(crate::callers::song::endpoint::wipe_data_from_song_queue), - ) - .route( - crate::callers::endpoints::QUEUEMETADATA, - post(crate::callers::metadata::endpoint::queue_metadata), - ) - .route( - crate::callers::endpoints::QUEUEMETADATA, - get(crate::callers::metadata::endpoint::fetch_metadata), - ) - .route( - crate::callers::endpoints::QUEUECOVERART, - post(crate::callers::coverart::endpoint::queue), - ) - .route( - crate::callers::endpoints::QUEUECOVERARTDATA, - get(crate::callers::coverart::endpoint::fetch_coverart_with_data), - ) - .route( - crate::callers::endpoints::QUEUECOVERART, - get(crate::callers::coverart::endpoint::fetch_coverart_no_data), - ) - .route( - crate::callers::endpoints::QUEUECOVERARTLINK, - patch(crate::callers::coverart::endpoint::link), - ) - .route( - crate::callers::endpoints::QUEUECOVERARTDATAWIPE, - patch(crate::callers::coverart::endpoint::wipe_data_from_coverart_queue), - ) .route( crate::callers::endpoints::CREATESONG, - post(crate::callers::song::endpoint::create_metadata), + post(crate::callers::song::endpoint::create_metadata).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::CREATECOVERART, - post(crate::callers::coverart::endpoint::create_coverart), + post(crate::callers::coverart::endpoint::create_coverart).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::GETSONGS, - get(crate::callers::song::endpoint::get_songs), + get(crate::callers::song::endpoint::get_songs).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::GETCOVERART, - get(crate::callers::coverart::endpoint::get_coverart), + get(crate::callers::coverart::endpoint::get_coverart).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::DOWNLOADCOVERART, - get(crate::callers::coverart::endpoint::download_coverart), + get(crate::callers::coverart::endpoint::download_coverart).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::STREAMSONG, - get(crate::callers::song::endpoint::stream_song), + get(crate::callers::song::endpoint::stream_song).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::DOWNLOADSONG, - get(crate::callers::song::endpoint::download_song), + get(crate::callers::song::endpoint::download_song).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::DELETESONG, - delete(crate::callers::song::endpoint::delete_song), + delete(crate::callers::song::endpoint::delete_song).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) } -- 2.47.3 From b3a024bd53298909ba3ade8c2c1eeb577c1200b1 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Thu, 31 Jul 2025 23:17:00 -0400 Subject: [PATCH 18/19] Version bump --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2afa4fc..2c870c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -819,7 +819,7 @@ dependencies = [ [[package]] name = "icarus" -version = "0.1.100" +version = "0.1.101" dependencies = [ "axum", "axum-extra", diff --git a/Cargo.toml b/Cargo.toml index 92ec979..d4f288e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "icarus" -version = "0.1.100" +version = "0.1.101" edition = "2024" rust-version = "1.88" -- 2.47.3 From 2ae999550af7fa4972518389b86c2c4590d90a7f Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Thu, 31 Jul 2025 23:23:38 -0400 Subject: [PATCH 19/19] This should fix the pipeline test failure --- .github/workflows/workflow.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 20cb1bd..711b72c 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -68,7 +68,9 @@ jobs: env: ROOT_DIRECTORY: "/tmp" DATABASE_URL: "postgres://${{ secrets.POSTGRES_USER }}:${{ secrets.POSTGRES_PASSWORD }}@localhost:5432/${{ secrets.POSTGRES_DB }}" - run: cargo test --verbose -- + run: | + cat .env.sample | head -1 > .env + cargo test --verbose -- - name: Run clippy run: cargo clippy -- -D warnings -- 2.47.3