diff --git a/Cargo.lock b/Cargo.lock index c83108d..8a069ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,41 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - [[package]] name = "aho-corasick" version = "1.1.2" @@ -82,7 +47,7 @@ version = "0.1.0" dependencies = [ "axum", "axum-htmx", - "axum_oidc", + "axum-oidc", "dotenvy", "env_logger", "futures-util", @@ -95,7 +60,9 @@ dependencies = [ "tokio", "tokio-util", "toml", + "tower", "tower-http", + "tower-sessions", ] [[package]] @@ -165,28 +132,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "axum-extra" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93e433be9382c737320af3924f7d5fc6f89c155cf2bf88949d8f5126fab283f" -dependencies = [ - "axum", - "axum-core", - "bytes", - "cookie", - "futures-util", - "http", - "http-body", - "mime", - "pin-project-lite", - "serde", - "tokio", - "tower", - "tower-layer", - "tower-service", -] - [[package]] name = "axum-htmx" version = "0.4.0" @@ -197,19 +142,21 @@ dependencies = [ ] [[package]] -name = "axum_oidc" -version = "0.1.0" -source = "git+https://git2.zettoit.eu/pfz4/axum_oidc#3b9438d1f3cf58c1edb9f503aea4e6d7783194d2" +name = "axum-oidc" +version = "0.0.0" dependencies = [ "async-trait", "axum", - "axum-extra", - "jsonwebtoken", + "axum-core", + "futures-util", + "http", "openidconnect", "reqwest", "serde", - "serde_json", "thiserror", + "tower-layer", + "tower-service", + "tower-sessions", ] [[package]] @@ -332,16 +279,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "color_quant" version = "1.1.0" @@ -360,11 +297,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" dependencies = [ - "aes-gcm", - "base64 0.21.5", "percent-encoding", - "rand", - "subtle", "time", "version_check", ] @@ -413,19 +346,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core", "typenum", ] -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - [[package]] name = "curve25519-dalek" version = "4.1.1" @@ -489,6 +412,19 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.2", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "der" version = "0.7.8" @@ -679,6 +615,20 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.29" @@ -686,6 +636,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -732,6 +683,7 @@ dependencies = [ "futures-core", "futures-io", "futures-macro", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -763,16 +715,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "ghash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" -dependencies = [ - "opaque-debug", - "polyval", -] - [[package]] name = "gimli" version = "0.28.0" @@ -1019,15 +961,6 @@ dependencies = [ "serde", ] -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array", -] - [[package]] name = "ipnet" version = "2.9.0" @@ -1075,20 +1008,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "jsonwebtoken" -version = "8.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" -dependencies = [ - "base64 0.21.5", - "pem", - "ring 0.16.20", - "serde", - "serde_json", - "simple_asn1", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -1124,6 +1043,7 @@ checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", + "serde", ] [[package]] @@ -1198,17 +1118,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -1313,12 +1222,6 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "openidconnect" version = "3.4.0" @@ -1407,15 +1310,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "pem" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" -dependencies = [ - "base64 0.13.1", -] - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -1490,18 +1384,6 @@ version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" -[[package]] -name = "polyval" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -1678,21 +1560,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - [[package]] name = "ring" version = "0.17.5" @@ -1703,7 +1570,7 @@ dependencies = [ "getrandom", "libc", "spin 0.9.8", - "untrusted 0.9.0", + "untrusted", "windows-sys", ] @@ -1762,7 +1629,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" dependencies = [ "log", - "ring 0.17.5", + "ring", "rustls-webpki", "sct", ] @@ -1782,8 +1649,8 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.5", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] @@ -1848,8 +1715,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.5", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] @@ -2012,18 +1879,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "simple_asn1" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror", - "time", -] - [[package]] name = "slab" version = "0.4.9" @@ -2308,6 +2163,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-cookies" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40f38d941a2ffd8402b36e02ae407637a9caceb693aaf2edc910437db0f36984" +dependencies = [ + "async-trait", + "axum-core", + "cookie", + "futures-util", + "http", + "parking_lot", + "pin-project-lite", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-http" version = "0.4.4" @@ -2345,6 +2217,28 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +[[package]] +name = "tower-sessions" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e45e0577e58fe796e44d3f531d26af3454caf8a1b0434f85bc835e8821d61986" +dependencies = [ + "async-trait", + "axum-core", + "dashmap", + "futures", + "http", + "parking_lot", + "serde", + "serde_json", + "thiserror", + "time", + "tower-cookies", + "tower-layer", + "tower-service", + "uuid", +] + [[package]] name = "tracing" version = "0.1.40" @@ -2407,22 +2301,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "untrusted" version = "0.9.0" @@ -2441,6 +2319,16 @@ dependencies = [ "serde", ] +[[package]] +name = "uuid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" +dependencies = [ + "getrandom", + "serde", +] + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 0f22ac2..333a590 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ tokio = { version = "1.33", features = ["full"] } tokio-util = "0.7" futures-util = "0.3" axum = { version = "0.6", features = ["multipart"] } -axum_oidc = { git="https://git2.zettoit.eu/pfz4/axum_oidc" } +axum-oidc = "0.1.0" axum-htmx = "0.4" tower-http = { version= "0.4", features = ["fs"] } sailfish = "0.8" @@ -22,3 +22,5 @@ serde = "1.0" toml = "0.8" rand = "0.8" qrcode = "0.12" +tower = "0.4.13" +tower-sessions = "0.4.1" diff --git a/src/main.rs b/src/main.rs index 7c53085..67f29c1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,17 +8,22 @@ use std::{ use axum::{ async_trait, + body::HttpBody, + error_handling::HandleErrorLayer, extract::{FromRef, Multipart, Path, Query, State}, - http::Uri, + http::{Request, StatusCode, Uri}, response::{ sse::{Event, KeepAlive}, Html, IntoResponse, Redirect, Sse, }, routing::get, - Form, Router, + BoxError, Form, Router, }; use axum_htmx::{HxRedirect, HxRequest}; -use axum_oidc::oidc::{self, EmptyAdditionalClaims, OidcApplication, OidcExtractor}; +use axum_oidc::{ + error::MiddlewareError, EmptyAdditionalClaims, OidcAuthLayer, OidcClaims, OidcClient, + OidcLoginLayer, +}; use futures_util::Stream; use game::Game; use garbage_collector::{start_gc, GarbageCollectorItem}; @@ -28,7 +33,9 @@ use sailfish::TemplateOnce; use serde::{Deserialize, Serialize}; use stream::{PlayerBroadcastStream, ViewerBroadcastStream}; use tokio::sync::RwLock; +use tower::{Layer, ServiceBuilder}; use tower_http::services::ServeDir; +use tower_sessions::{cookie::SameSite, Expiry, MemoryStore, SessionManagerLayer}; use crate::error::Error; @@ -45,16 +52,9 @@ mod question; pub struct AppState { games: Arc>>, game_expiry: Arc>>, - oidc_application: OidcApplication, application_base: String, } -impl FromRef for OidcApplication { - fn from_ref(input: &AppState) -> Self { - input.oidc_application.clone() - } -} - #[tokio::main] pub async fn main() { dotenvy::dotenv().ok(); @@ -70,18 +70,34 @@ pub async fn main() { .map(|x| x.to_owned()) .collect::>(); - let oidc_application = OidcApplication::::create( - application_base - .parse() - .expect("valid APPLICATION_BASE url"), - issuer.to_string(), - client_id.to_string(), - client_secret.to_owned(), - scopes.clone(), - oidc::Key::generate(), - ) - .await - .expect("Oidc Authentication Client"); + let session_store = MemoryStore::default(); + let session_service = ServiceBuilder::new() + .layer(HandleErrorLayer::new(|_: BoxError| async { + StatusCode::BAD_REQUEST + })) + .layer(SessionManagerLayer::new(session_store).with_same_site(SameSite::Lax)); + + let oidc_login_service = ServiceBuilder::new() + .layer(HandleErrorLayer::new(|e: MiddlewareError| async { + e.into_response() + })) + .layer(OidcLoginLayer::::new()); + + let oidc_auth_service = ServiceBuilder::new() + .layer(HandleErrorLayer::new(|e: MiddlewareError| async { + e.into_response() + })) + .layer( + OidcAuthLayer::::discover_client( + Uri::from_maybe_shared(application_base.clone()).expect("valid APPLICATION_BASE"), + issuer.to_string(), + client_id.to_string(), + client_secret.to_owned(), + scopes.clone(), + ) + .await + .expect("OIDC Client"), + ); let game_expiry: Arc>> = Arc::new(RwLock::new(BinaryHeap::new())); @@ -92,18 +108,20 @@ pub async fn main() { let app_state = AppState { games, game_expiry, - oidc_application, application_base, }; let app = Router::new() .route("/", get(handle_index).post(handle_create)) - .route("/:id", get(handle_player).post(handle_player_answer)) - .route("/:id/events", get(sse_player)) .route("/:id/view", get(handle_view).post(handle_view_next)) .route("/:id/view/events", get(sse_view)) + .layer(oidc_login_service) + .route("/:id", get(handle_player).post(handle_player_answer)) + .route("/:id/events", get(sse_player)) .nest_service("/static", ServeDir::new("static")) - .with_state(app_state); + .with_state(app_state) + .layer(oidc_auth_service) + .layer(session_service); axum::Server::bind(&"[::]:8080".parse().expect("valid listen address")) .serve(app.into_make_service()) @@ -111,15 +129,13 @@ pub async fn main() { .expect("axum server"); } -pub async fn handle_index( - oidc_extractor: OidcExtractor, -) -> HandlerResult { +pub async fn handle_index() -> HandlerResult { Ok(Html(IndexTemplate {}.render_once()?)) } pub async fn handle_create( State(state): State, - oidc_extractor: OidcExtractor, + OidcClaims(claims): OidcClaims, mut body: Multipart, ) -> HandlerResult { let mut quiz: Option = None; @@ -137,11 +153,7 @@ pub async fn handle_create( .map(char::from) .collect(); - let game = Game::new( - game_id.clone(), - oidc_extractor.claims.subject().to_string(), - quiz, - ); + let game = Game::new(game_id.clone(), claims.subject().to_string(), quiz); let mut games = state.games.write().await; @@ -159,12 +171,12 @@ pub async fn handle_view( Path(id): Path, State(state): State, HxRequest(htmx): HxRequest, - oidc_extractor: OidcExtractor, + OidcClaims(claims): OidcClaims, ) -> HandlerResult { let games = state.games.read().await; let game = games.get(&id).ok_or(Error::NotFound)?; - if game.owner != oidc_extractor.claims.subject().to_string() { + if game.owner != claims.subject().to_string() { return Err(Error::Forbidden); } @@ -175,12 +187,12 @@ pub async fn handle_view_next( Path(id): Path, State(state): State, HxRequest(htmx): HxRequest, - oidc_extractor: OidcExtractor, + OidcClaims(claims): OidcClaims, ) -> HandlerResult { let mut games = state.games.write().await; let game = games.get_mut(&id).ok_or(Error::NotFound)?; - if game.owner != oidc_extractor.claims.subject().to_string() { + if game.owner != claims.subject().to_string() { return Err(Error::Forbidden); } @@ -192,12 +204,12 @@ pub async fn handle_view_next( pub async fn sse_view( Path(id): Path, State(state): State, - oidc_extractor: OidcExtractor, + OidcClaims(claims): OidcClaims, ) -> HandlerResult>>> { let games = state.games.read().await; let game = games.get(&id).ok_or(Error::NotFound)?; - if game.owner != oidc_extractor.claims.subject().to_string() { + if game.owner != claims.subject().to_string() { return Err(Error::Forbidden); }