diff --git a/Cargo.lock b/Cargo.lock index 1f33cc4..afea94e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1151,6 +1151,7 @@ dependencies = [ "tracing", "tracing-opentelemetry", "tracing-subscriber", + "urlencoding", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a31c2db..15b6cd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,3 +36,4 @@ include_dir = "0.7.3" serde-env = "0.2.0" tonic = { version = "0.12.3", features = ["tls", "tls-webpki-roots"] } secretfile = "0.1.0" +urlencoding = "2.1.3" diff --git a/flake.lock b/flake.lock index 7c42556..9735670 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "crane": { "locked": { - "lastModified": 1774313767, - "narHash": "sha256-hy0XTQND6avzGEUFrJtYBBpFa/POiiaGBr2vpU6Y9tY=", + "lastModified": 1780099841, + "narHash": "sha256-EVZd2RsbpreRUDSi9rBwPY+ZxoyMaiEBbZxxhljbaS4=", "owner": "ipetkov", "repo": "crane", - "rev": "3d9df76e29656c679c744968b17fbaf28f0e923d", + "rev": "0532eb17955225173906d671fb36306bdeb1e2dc", "type": "github" }, "original": { @@ -22,11 +22,11 @@ ] }, "locked": { - "lastModified": 1774271954, - "narHash": "sha256-FbvMOykx7f7uEPdRVzUSABnLjqCdEp22wa0nDkuEd3s=", + "lastModified": 1784556984, + "narHash": "sha256-I7BrF/EFE5mOL7AQTWBl/1yb7jSPjURcBvpFfP/fJm0=", "owner": "nix-community", "repo": "flakelight", - "rev": "c90878b309508083094f465d6aa11b3963f48b9f", + "rev": "8150a22dcc9c11f6487687abe2077aa7c2aa1ab6", "type": "github" }, "original": { @@ -44,11 +44,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1774538603, - "narHash": "sha256-IG0fOFNUjselW61zc/jOZU3abmt7FjcRdES9SXeHun4=", + "lastModified": 1780231986, + "narHash": "sha256-OyafczPtzE0Xa2zl3j/KvV2+ZVYGhYQHt0MOVWtDXlY=", "ref": "refs/heads/main", - "rev": "0ef9ce3384617243e1b9e94a7057f879f621f014", - "revCount": 69, + "rev": "f5cbda29b945df03256bf63c22fa4cd5fa429e67", + "revCount": 72, "type": "git", "url": "https://codeberg.org/icewind/mill-scale.git" }, @@ -59,16 +59,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1774388614, - "narHash": "sha256-tFwzTI0DdDzovdE9+Ras6CUss0yn8P9XV4Ja6RjA+nU=", + "lastModified": 1784856561, + "narHash": "sha256-J+Bx1Z6Oeoj2FgnBhRMKyUhhtDoOpTgXYaVLZpDjW4A=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "1073dad219cb244572b74da2b20c7fe39cb3fa9e", + "rev": "597283ad8aa0b331c788e97c4c262d58877074ef", "type": "github" }, "original": { "id": "nixpkgs", - "ref": "nixos-25.11", + "ref": "nixos-26.05", "type": "indirect" } }, @@ -88,11 +88,11 @@ ] }, "locked": { - "lastModified": 1774535687, - "narHash": "sha256-dpKS/8+uB0EoI4mCrpio+xs8Xxry6ZhLLwV8VIbbfrs=", + "lastModified": 1780197589, + "narHash": "sha256-FVCr2Ij/jKf59a4LW481eeOF6rJRreOBrVgW/aUBTrw=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "75900435aa883f84b038316864b3f60956681523", + "rev": "21632e942d89bf1cce4e5a63d7e58a215a0cbfcc", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index d8c1d7a..63b049f 100644 --- a/flake.nix +++ b/flake.nix @@ -1,6 +1,6 @@ { inputs = { - nixpkgs.url = "nixpkgs/nixos-25.11"; + nixpkgs.url = "nixpkgs/nixos-26.05"; flakelight = { url = "github:nix-community/flakelight"; inputs.nixpkgs.follows = "nixpkgs"; @@ -29,13 +29,12 @@ }) (import ./nix/overlay.nix) ]; - toolchain = pkgs: pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; tools = pkgs: with pkgs; [ bacon cargo-edit nodejs - nodePackages.svgo + svgo typescript sqlx-cli ]; diff --git a/src/main.rs b/src/main.rs index 1e28809..89faaed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,7 +32,7 @@ use axum::http::header::{CONTENT_TYPE, ETAG, LOCATION, SET_COOKIE}; use axum::http::{HeaderValue, Request, StatusCode}; use axum::response::IntoResponse; use axum::{extract::State, routing::get, serve, Router}; -use axum_extra::headers::Cookie; +use axum_extra::headers::{Cookie, Referer}; use axum_extra::TypedHeader; use demostf_build::Asset; pub use error::Error; @@ -49,6 +49,7 @@ use opentelemetry::KeyValue; use opentelemetry_otlp::{SpanExporter, WithExportConfig, WithTonicConfig}; use opentelemetry_sdk::trace::SdkTracerProvider; use opentelemetry_sdk::Resource; +use reqwest::Url; use secretfile::load; use sqlx::PgPool; use std::convert::Infallible; @@ -67,6 +68,7 @@ use tonic::transport::{ClientTlsConfig, Identity}; use tower_http::trace::TraceLayer; use tracing::{error, info, info_span, instrument}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer}; +use urlencoding::Encoded; pub type Result = std::result::Result; @@ -77,6 +79,7 @@ struct App { maps: String, sync: String, map_list: MapList, + url: String, pub session_store: MemoryStore, } @@ -160,6 +163,7 @@ async fn main() -> Result<()> { api: config.site.api, maps: config.site.maps, sync: config.site.sync, + url: config.site.url, map_list, session_store: session_store.clone(), }); @@ -195,6 +199,7 @@ async fn main() -> Result<()> { .route("/about", get(about)) .route("/api", get(api)) .route("/login/callback", get(login_callback)) + .route("/login/callback/{return_to}", get(login_callback)) .route("/login", get(login)) .route("/logout", get(logout)) .route("/upload", get(upload)) @@ -358,9 +363,17 @@ async fn demo( async fn login_callback( State(app): State>, RawQuery(query): RawQuery, + return_to: Option>, ) -> Result { + let referrer_openid = return_to + .as_ref() + .map(|return_to| format!("/login/callback/{}", Encoded(return_to.as_str()))) + .map(|callback| SteamOpenId::new(&app.url, &callback).ok()) + .flatten(); + let openid = referrer_openid.as_ref().unwrap_or(&app.openid); + let query = query.as_deref().unwrap_or_default(); - let steam_id = app.openid.verify(query).await.map_err(|e| { + let steam_id = openid.verify(query).await.map_err(|e| { error!("{e:?}"); Error::SteamAuth })?; @@ -376,6 +389,14 @@ async fn login_callback( .store_session(session) .await? .unwrap_or_default(); + + let return_to = return_to + .as_ref() + .map(|Path(return_to)| return_to) + .map(|return_to| HeaderValue::from_str(&return_to).ok()) + .flatten() + .unwrap_or(HeaderValue::from_static("/")); + Ok(( StatusCode::FOUND, [ @@ -387,20 +408,35 @@ async fn login_callback( )) .expect("invalid cookie"), ), - (LOCATION, HeaderValue::from_static("/")), + (LOCATION, return_to), ], )) } #[instrument(skip(app))] #[axum::debug_handler] -async fn login(State(app): State>) -> impl IntoResponse { +async fn login( + State(app): State>, + referrer: Option>, +) -> impl IntoResponse { + let referrer = referrer + .map(|TypedHeader(referrer)| referrer.to_string()) + .map(|referrer| Url::parse(&referrer).ok()) + .flatten(); + + let referrer_openid = referrer + .as_ref() + .map(|referrer| format!("/login/callback/{}", Encoded(referrer.path()))) + .map(|callback| SteamOpenId::new(&app.url, &callback).ok()) + .flatten(); + let openid = referrer_openid.as_ref().unwrap_or(&app.openid); ( StatusCode::FOUND, [( LOCATION, - HeaderValue::from_str(app.openid.get_redirect_url()).unwrap(), + HeaderValue::from_str(openid.get_redirect_url()).unwrap(), )], + "", ) }