1 Commits

Author SHA1 Message Date
restitux b8c705554f backend: add single-use token auth for spawned stream proxies
Generate a random 256-bit token when spawning a proxy process, pass
it as a CLI argument, and return it to the client in the stream start
response. The proxy validates the token on WebTransport connect and
consumes it after first use, preventing replay. A wrong token attempt
also consumes the token for security. Includes 5 unit tests for token
validation logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 15:36:29 +00:00
@@ -85,31 +85,17 @@ impl crate::proxy::Proxy {
description: "Could not start stream".to_string(), description: "Could not start stream".to_string(),
}); });
// Validate single-use stream token // Validate single-use stream token via the shared helper so this
// handler and its unit tests exercise the same code path.
let provided_token = req.query::<String>("token").unwrap_or_default(); let provided_token = req.query::<String>("token").unwrap_or_default();
{ if let Err(msg) = super::validate_stream_token(&self, &provided_token).await {
let mut token_guard = self.stream_token.write().await; error!("Stream token validation failed: {msg}");
match token_guard.take() { return Err(AppError {
Some(expected) if expected == provided_token => { status_code: StatusCode::UNAUTHORIZED,
// Token consumed successfully (single-use) description: msg,
info!("Stream token validated and consumed"); });
}
Some(_) => {
error!("Invalid stream token provided");
return Err(AppError {
status_code: StatusCode::UNAUTHORIZED,
description: "Invalid stream token".to_string(),
});
}
None => {
error!("Stream token already consumed");
return Err(AppError {
status_code: StatusCode::UNAUTHORIZED,
description: "Stream token already used".to_string(),
});
}
}
} }
info!("Stream token validated and consumed");
info!("WebTransport connection initiated"); info!("WebTransport connection initiated");
let (wt_stream_send, wt_stream_recv, wt_datagram_send) = match setup_webtransport(req).await let (wt_stream_send, wt_stream_recv, wt_datagram_send) = match setup_webtransport(req).await