redirect back to original page on login

This commit is contained in:
Robin Appelman 2026-07-25 22:32:12 +02:00
commit 3bd791f047
5 changed files with 62 additions and 25 deletions

1
Cargo.lock generated
View file

@ -1151,6 +1151,7 @@ dependencies = [
"tracing", "tracing",
"tracing-opentelemetry", "tracing-opentelemetry",
"tracing-subscriber", "tracing-subscriber",
"urlencoding",
] ]
[[package]] [[package]]

View file

@ -36,3 +36,4 @@ include_dir = "0.7.3"
serde-env = "0.2.0" serde-env = "0.2.0"
tonic = { version = "0.12.3", features = ["tls", "tls-webpki-roots"] } tonic = { version = "0.12.3", features = ["tls", "tls-webpki-roots"] }
secretfile = "0.1.0" secretfile = "0.1.0"
urlencoding = "2.1.3"

34
flake.lock generated
View file

@ -2,11 +2,11 @@
"nodes": { "nodes": {
"crane": { "crane": {
"locked": { "locked": {
"lastModified": 1774313767, "lastModified": 1780099841,
"narHash": "sha256-hy0XTQND6avzGEUFrJtYBBpFa/POiiaGBr2vpU6Y9tY=", "narHash": "sha256-EVZd2RsbpreRUDSi9rBwPY+ZxoyMaiEBbZxxhljbaS4=",
"owner": "ipetkov", "owner": "ipetkov",
"repo": "crane", "repo": "crane",
"rev": "3d9df76e29656c679c744968b17fbaf28f0e923d", "rev": "0532eb17955225173906d671fb36306bdeb1e2dc",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -22,11 +22,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1774271954, "lastModified": 1784556984,
"narHash": "sha256-FbvMOykx7f7uEPdRVzUSABnLjqCdEp22wa0nDkuEd3s=", "narHash": "sha256-I7BrF/EFE5mOL7AQTWBl/1yb7jSPjURcBvpFfP/fJm0=",
"owner": "nix-community", "owner": "nix-community",
"repo": "flakelight", "repo": "flakelight",
"rev": "c90878b309508083094f465d6aa11b3963f48b9f", "rev": "8150a22dcc9c11f6487687abe2077aa7c2aa1ab6",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -44,11 +44,11 @@
"rust-overlay": "rust-overlay" "rust-overlay": "rust-overlay"
}, },
"locked": { "locked": {
"lastModified": 1774538603, "lastModified": 1780231986,
"narHash": "sha256-IG0fOFNUjselW61zc/jOZU3abmt7FjcRdES9SXeHun4=", "narHash": "sha256-OyafczPtzE0Xa2zl3j/KvV2+ZVYGhYQHt0MOVWtDXlY=",
"ref": "refs/heads/main", "ref": "refs/heads/main",
"rev": "0ef9ce3384617243e1b9e94a7057f879f621f014", "rev": "f5cbda29b945df03256bf63c22fa4cd5fa429e67",
"revCount": 69, "revCount": 72,
"type": "git", "type": "git",
"url": "https://codeberg.org/icewind/mill-scale.git" "url": "https://codeberg.org/icewind/mill-scale.git"
}, },
@ -59,16 +59,16 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1774388614, "lastModified": 1784856561,
"narHash": "sha256-tFwzTI0DdDzovdE9+Ras6CUss0yn8P9XV4Ja6RjA+nU=", "narHash": "sha256-J+Bx1Z6Oeoj2FgnBhRMKyUhhtDoOpTgXYaVLZpDjW4A=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "1073dad219cb244572b74da2b20c7fe39cb3fa9e", "rev": "597283ad8aa0b331c788e97c4c262d58877074ef",
"type": "github" "type": "github"
}, },
"original": { "original": {
"id": "nixpkgs", "id": "nixpkgs",
"ref": "nixos-25.11", "ref": "nixos-26.05",
"type": "indirect" "type": "indirect"
} }
}, },
@ -88,11 +88,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1774535687, "lastModified": 1780197589,
"narHash": "sha256-dpKS/8+uB0EoI4mCrpio+xs8Xxry6ZhLLwV8VIbbfrs=", "narHash": "sha256-FVCr2Ij/jKf59a4LW481eeOF6rJRreOBrVgW/aUBTrw=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "75900435aa883f84b038316864b3f60956681523", "rev": "21632e942d89bf1cce4e5a63d7e58a215a0cbfcc",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -1,6 +1,6 @@
{ {
inputs = { inputs = {
nixpkgs.url = "nixpkgs/nixos-25.11"; nixpkgs.url = "nixpkgs/nixos-26.05";
flakelight = { flakelight = {
url = "github:nix-community/flakelight"; url = "github:nix-community/flakelight";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@ -29,13 +29,12 @@
}) })
(import ./nix/overlay.nix) (import ./nix/overlay.nix)
]; ];
toolchain = pkgs: pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
tools = pkgs: tools = pkgs:
with pkgs; [ with pkgs; [
bacon bacon
cargo-edit cargo-edit
nodejs nodejs
nodePackages.svgo svgo
typescript typescript
sqlx-cli sqlx-cli
]; ];

View file

@ -32,7 +32,7 @@ use axum::http::header::{CONTENT_TYPE, ETAG, LOCATION, SET_COOKIE};
use axum::http::{HeaderValue, Request, StatusCode}; use axum::http::{HeaderValue, Request, StatusCode};
use axum::response::IntoResponse; use axum::response::IntoResponse;
use axum::{extract::State, routing::get, serve, Router}; use axum::{extract::State, routing::get, serve, Router};
use axum_extra::headers::Cookie; use axum_extra::headers::{Cookie, Referer};
use axum_extra::TypedHeader; use axum_extra::TypedHeader;
use demostf_build::Asset; use demostf_build::Asset;
pub use error::Error; pub use error::Error;
@ -49,6 +49,7 @@ use opentelemetry::KeyValue;
use opentelemetry_otlp::{SpanExporter, WithExportConfig, WithTonicConfig}; use opentelemetry_otlp::{SpanExporter, WithExportConfig, WithTonicConfig};
use opentelemetry_sdk::trace::SdkTracerProvider; use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_sdk::Resource; use opentelemetry_sdk::Resource;
use reqwest::Url;
use secretfile::load; use secretfile::load;
use sqlx::PgPool; use sqlx::PgPool;
use std::convert::Infallible; use std::convert::Infallible;
@ -67,6 +68,7 @@ use tonic::transport::{ClientTlsConfig, Identity};
use tower_http::trace::TraceLayer; use tower_http::trace::TraceLayer;
use tracing::{error, info, info_span, instrument}; use tracing::{error, info, info_span, instrument};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer};
use urlencoding::Encoded;
pub type Result<T, E = Error> = std::result::Result<T, E>; pub type Result<T, E = Error> = std::result::Result<T, E>;
@ -77,6 +79,7 @@ struct App {
maps: String, maps: String,
sync: String, sync: String,
map_list: MapList, map_list: MapList,
url: String,
pub session_store: MemoryStore, pub session_store: MemoryStore,
} }
@ -160,6 +163,7 @@ async fn main() -> Result<()> {
api: config.site.api, api: config.site.api,
maps: config.site.maps, maps: config.site.maps,
sync: config.site.sync, sync: config.site.sync,
url: config.site.url,
map_list, map_list,
session_store: session_store.clone(), session_store: session_store.clone(),
}); });
@ -195,6 +199,7 @@ async fn main() -> Result<()> {
.route("/about", get(about)) .route("/about", get(about))
.route("/api", get(api)) .route("/api", get(api))
.route("/login/callback", get(login_callback)) .route("/login/callback", get(login_callback))
.route("/login/callback/{return_to}", get(login_callback))
.route("/login", get(login)) .route("/login", get(login))
.route("/logout", get(logout)) .route("/logout", get(logout))
.route("/upload", get(upload)) .route("/upload", get(upload))
@ -358,9 +363,17 @@ async fn demo(
async fn login_callback( async fn login_callback(
State(app): State<Arc<App>>, State(app): State<Arc<App>>,
RawQuery(query): RawQuery, RawQuery(query): RawQuery,
return_to: Option<Path<String>>,
) -> Result<impl IntoResponse> { ) -> Result<impl IntoResponse> {
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 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!("{e:?}");
Error::SteamAuth Error::SteamAuth
})?; })?;
@ -376,6 +389,14 @@ async fn login_callback(
.store_session(session) .store_session(session)
.await? .await?
.unwrap_or_default(); .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(( Ok((
StatusCode::FOUND, StatusCode::FOUND,
[ [
@ -387,20 +408,35 @@ async fn login_callback(
)) ))
.expect("invalid cookie"), .expect("invalid cookie"),
), ),
(LOCATION, HeaderValue::from_static("/")), (LOCATION, return_to),
], ],
)) ))
} }
#[instrument(skip(app))] #[instrument(skip(app))]
#[axum::debug_handler] #[axum::debug_handler]
async fn login(State(app): State<Arc<App>>) -> impl IntoResponse { async fn login(
State(app): State<Arc<App>>,
referrer: Option<TypedHeader<Referer>>,
) -> 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, StatusCode::FOUND,
[( [(
LOCATION, LOCATION,
HeaderValue::from_str(app.openid.get_redirect_url()).unwrap(), HeaderValue::from_str(openid.get_redirect_url()).unwrap(),
)], )],
"",
) )
} }