diff options
author | rtkay123 <dev@kanjala.com> | 2025-07-25 18:37:32 +0200 |
---|---|---|
committer | rtkay123 <dev@kanjala.com> | 2025-07-25 18:37:32 +0200 |
commit | 3406efe921e877878d8ba656651e0d90382586ae (patch) | |
tree | 72538576df711385490d12a4ef9fed0781ce9de2 | |
parent | 3831e5a42ab4e21f116537c5251582245de37f0b (diff) | |
download | sellershut-3406efe921e877878d8ba656651e0d90382586ae.tar.bz2 sellershut-3406efe921e877878d8ba656651e0d90382586ae.zip |
feat(auth): validate token
-rw-r--r-- | Cargo.lock | 295 | ||||
-rw-r--r-- | Cargo.toml | 4 | ||||
-rw-r--r-- | crates/auth/Cargo.toml | 5 | ||||
-rw-r--r-- | crates/auth/src/auth.rs | 12 | ||||
-rw-r--r-- | crates/auth/src/main.rs | 41 | ||||
-rw-r--r-- | crates/auth/src/server.rs | 1 | ||||
-rw-r--r-- | crates/auth/src/server/grpc.rs | 2 | ||||
-rw-r--r-- | crates/auth/src/server/grpc/auth.rs | 50 | ||||
-rw-r--r-- | crates/auth/src/server/grpc/interceptor.rs | 11 | ||||
-rw-r--r-- | crates/auth/src/server/routes/authorised.rs | 11 |
10 files changed, 418 insertions, 14 deletions
@@ -185,6 +185,12 @@ dependencies = [ ] [[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] name = "auth-service" version = "0.1.0" dependencies = [ @@ -199,12 +205,15 @@ dependencies = [ "nanoid", "oauth2", "reqwest", + "sellershut-core", "serde", "serde_json", "sqlx", "stack-up", "time", "tokio", + "tonic", + "tonic-reflection", "tower", "tower-http", "tower-sessions", @@ -764,6 +773,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] name = "etcetera" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -796,6 +815,18 @@ dependencies = [ ] [[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] name = "flume" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1000,6 +1031,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] +name = "h2" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] name = "hashbrown" version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1163,6 +1213,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2", "http", "http-body", "httparse", @@ -1192,6 +1243,19 @@ dependencies = [ ] [[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] name = "hyper-util" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1467,6 +1531,12 @@ dependencies = [ ] [[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] name = "litemap" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1588,6 +1658,12 @@ dependencies = [ ] [[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] name = "nanoid" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1821,6 +1897,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1909,6 +2015,80 @@ dependencies = [ ] [[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "pulldown-cmark", + "pulldown-cmark-to-cmark", + "regex", + "syn 2.0.104", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost", +] + +[[package]] +name = "pulldown-cmark" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] +name = "pulldown-cmark-to-cmark" +version = "20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c0f333311d2d8fda65bcf76af35054e9f38e253332a0289746156a59656988b" +dependencies = [ + "pulldown-cmark", +] + +[[package]] name = "quinn" version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2224,6 +2404,19 @@ dependencies = [ ] [[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] name = "rustls" version = "0.23.29" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2322,6 +2515,19 @@ dependencies = [ ] [[package]] +name = "sellershut-core" +version = "0.1.0" +dependencies = [ + "prost", + "serde", + "serde_json", + "time", + "tonic", + "tonic-build", + "tonic-types", +] + +[[package]] name = "semver" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2795,6 +3001,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" [[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2995,6 +3214,73 @@ dependencies = [ ] [[package]] +name = "tonic" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9" +dependencies = [ + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "socket2", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac6f67be712d12f0b41328db3137e0d0757645d8904b4cb7d51cd9c2279e847" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tonic-reflection" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9687bd5bfeafebdded2356950f278bba8226f0b32109537c4253406e09aafe1" +dependencies = [ + "prost", + "prost-types", + "tokio", + "tokio-stream", + "tonic", +] + +[[package]] +name = "tonic-types" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07439468da24d5f211d3f3bd7b63665d8f45072804457e838a87414a478e2db8" +dependencies = [ + "prost", + "prost-types", + "tonic", +] + +[[package]] name = "tower" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3002,9 +3288,12 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", + "indexmap", "pin-project-lite", + "slab", "sync_wrapper", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -3209,6 +3498,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] name = "unicode-bidi" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1,5 +1,5 @@ [workspace] -members = ["crates/*"] +members = ["crates/*", "lib/*"] resolver = "3" [workspace.package] @@ -18,12 +18,14 @@ config = { version = "0.15.13", default-features = false } futures-util = { version = "0.3.31", default-features = false } nanoid = "0.4.0" reqwest = { version = "0.12.22", default-features = false } +sellershut-core = { path = "lib/sellershut-core/" } serde = "1.0.219" serde_json = "1.0.140" sqlx = "0.8.6" stack-up = { git = "https://github.com/rtkay123/stack-up.git" } time = { version = "0.3.41", default-features = false } tokio = "1.46.1" +tonic = "0.13.0" tower = "0.5.2" tower-http = "0.6.6" tracing = "0.1.41" diff --git a/crates/auth/Cargo.toml b/crates/auth/Cargo.toml index 410c51e..cc6d676 100644 --- a/crates/auth/Cargo.toml +++ b/crates/auth/Cargo.toml @@ -19,12 +19,15 @@ jsonwebtoken = "9.3.1" nanoid.workspace = true oauth2 = "5.0.0" reqwest = { workspace = true, features = ["json", "rustls-tls"] } +sellershut-core = { workspace = true, features = ["auth", "serde"] } serde = { workspace = true, features = ["derive"] } serde_json.workspace = true sqlx = { workspace = true, features = ["macros", "migrate", "runtime-tokio", "time", "tls-rustls", "uuid"] } time = { workspace = true, features = ["parsing", "serde"] } tokio = { workspace = true, features = ["macros", "rt-multi-thread", "signal"] } -tower = { workspace = true, features = ["util"] } +tonic.workspace = true +tonic-reflection = "0.13.0" +tower = { workspace = true, features = ["steer", "util"] } tower-http = { workspace = true, features = ["map-request-body", "trace", "util"] } tower-sessions = "0.14.0" tower-sessions-core = { version = "0.14.0", features = ["deletion-task"] } diff --git a/crates/auth/src/auth.rs b/crates/auth/src/auth.rs new file mode 100644 index 0000000..04cb60a --- /dev/null +++ b/crates/auth/src/auth.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Claims { + pub iss: String, + pub sub: Uuid, + pub exp: i64, + pub iat: i64, + pub sid: String, + pub aud: String, +} diff --git a/crates/auth/src/main.rs b/crates/auth/src/main.rs index a1883ad..72f991f 100644 --- a/crates/auth/src/main.rs +++ b/crates/auth/src/main.rs @@ -1,3 +1,4 @@ +mod auth; mod client; mod cnfg; mod error; @@ -7,11 +8,19 @@ mod state; use std::net::{Ipv6Addr, SocketAddr}; use clap::Parser; +use reqwest::header::CONTENT_TYPE; +use sellershut_core::auth::{AUTH_FILE_DESCRIPTOR_SET, auth_server::AuthServer}; use stack_up::{Configuration, Services, tracing::Tracing}; use tokio::{signal, task::AbortHandle}; +use tonic::service::Routes; +use tower::{make::Shared, steer::Steer}; use tracing::{info, trace}; -use crate::{error::AppError, state::AppState}; +use crate::{ + error::AppError, + server::{grpc::interceptor::MyInterceptor, routes::authorised::AuthRequest}, + state::AppState, +}; /// auth-service #[derive(Parser, Debug)] @@ -63,7 +72,35 @@ async fn main() -> Result<(), AppError> { let listener = tokio::net::TcpListener::bind(addr).await?; info!(port = addr.port(), "serving api"); - axum::serve(listener, server::router(state)) + let service = AuthServer::with_interceptor(state.clone(), MyInterceptor); + let auth_reflector = tonic_reflection::server::Builder::configure() + .register_encoded_file_descriptor_set(AUTH_FILE_DESCRIPTOR_SET) + .build_v1()?; + + let grpc_server = Routes::new(service) + .add_service(auth_reflector) + .into_axum_router(); + + let service = Steer::new( + vec![server::router(state), grpc_server], + |req: &axum::extract::Request, _services: &[_]| { + if req + .headers() + .get(CONTENT_TYPE) + .map(|content_type| content_type.as_bytes()) + .filter(|content_type| content_type.starts_with(b"application/grpc")) + .is_some() + { + // grpc service + 1 + } else { + // http service + 0 + } + }, + ); + + axum::serve(listener, Shared::new(service)) .with_graceful_shutdown(shutdown_signal(deletion_task.abort_handle())) .await?; diff --git a/crates/auth/src/server.rs b/crates/auth/src/server.rs index 2892412..7b66c42 100644 --- a/crates/auth/src/server.rs +++ b/crates/auth/src/server.rs @@ -7,6 +7,7 @@ use crate::{ }; pub mod csrf_token_validation; +pub mod grpc; pub mod routes; const CSRF_TOKEN: &str = "csrf_token"; diff --git a/crates/auth/src/server/grpc.rs b/crates/auth/src/server/grpc.rs new file mode 100644 index 0000000..0fd775b --- /dev/null +++ b/crates/auth/src/server/grpc.rs @@ -0,0 +1,2 @@ +pub mod auth; +pub mod interceptor; diff --git a/crates/auth/src/server/grpc/auth.rs b/crates/auth/src/server/grpc/auth.rs new file mode 100644 index 0000000..fb00291 --- /dev/null +++ b/crates/auth/src/server/grpc/auth.rs @@ -0,0 +1,50 @@ +use std::str::FromStr; + +use jsonwebtoken::DecodingKey; +use sellershut_core::auth::{ValidationRequest, ValidationResponse, auth_server::Auth}; +use tonic::{Request, Response, Status, async_trait}; +use tower_sessions::{SessionStore, session::Id}; +use tracing::warn; + +use crate::{auth::Claims, state::AppHandle}; + +#[async_trait] +impl Auth for AppHandle { + async fn validate_auth_token( + &self, + request: Request<ValidationRequest>, + ) -> Result<Response<ValidationResponse>, Status> { + let token = request.into_inner().token; + + let token = jsonwebtoken::decode::<Claims>( + &token, + &DecodingKey::from_secret(self.local_config.oauth.jwt_encoding_key.as_bytes()), + &jsonwebtoken::Validation::default(), + ); + + match token { + Ok(value) => { + let session_id = value.claims.sid; + let store = &self.session_store; + match Id::from_str(&session_id) { + Ok(ref id) => { + if let Ok(Some(_)) = store.load(id).await { + return Ok(Response::new(ValidationResponse { valid: true })); + } else { + return Ok(Response::new(Default::default())); + } + } + Err(e) => { + warn!("{e}"); + + return Ok(Response::new(Default::default())); + } + } + } + Err(e) => { + warn!("{e}"); + Ok(Response::new(ValidationResponse::default())) + } + } + } +} diff --git a/crates/auth/src/server/grpc/interceptor.rs b/crates/auth/src/server/grpc/interceptor.rs new file mode 100644 index 0000000..6fbe7fa --- /dev/null +++ b/crates/auth/src/server/grpc/interceptor.rs @@ -0,0 +1,11 @@ +use tonic::{Status, service::Interceptor}; +use tracing::Span; + +#[derive(Clone, Copy)] +pub struct MyInterceptor; + +impl Interceptor for MyInterceptor { + fn call(&mut self, request: tonic::Request<()>) -> Result<tonic::Request<()>, Status> { + Ok(request) + } +} diff --git a/crates/auth/src/server/routes/authorised.rs b/crates/auth/src/server/routes/authorised.rs index 27f02bc..50fcfc8 100644 --- a/crates/auth/src/server/routes/authorised.rs +++ b/crates/auth/src/server/routes/authorised.rs @@ -19,6 +19,7 @@ use tower_sessions::{ use uuid::Uuid; use crate::{ + auth::Claims, error::AppError, server::{ OAUTH_CSRF_COOKIE, csrf_token_validation::csrf_token_validation_workflow, routes::Provider, @@ -55,16 +56,6 @@ struct DbUser { const SESSION_COOKIE: &str = "info"; const SESSION_DATA_KEY: &str = "data"; -#[derive(Debug, Serialize, Deserialize)] -struct Claims { - iss: String, - sub: Uuid, - exp: i64, - iat: i64, - sid: String, - aud: String, -} - pub async fn login_authorised( Query(query): Query<AuthRequest>, State(state): State<AppHandle>, |