use axum::body::Body;
use axum::extract::{Path, State};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use didwebvh_rs::url::WebVHURL;
use crate::server::AppState;
const JSONL_CONTENT_TYPE: &str = "text/jsonl";
#[utoipa::path(
get, path = "/.well-known/did.jsonl", tag = "did-webvh",
responses(
(status = 200, description = "VTA did.jsonl log", content_type = "text/jsonl"),
(status = 404, description = "VTA has no self-hosted did:webvh identity at this path"),
),
)]
pub async fn get_vta_well_known_did_log_handler(State(state): State<AppState>) -> Response {
serve_canonical(&state, "/.well-known/did.jsonl").await
}
pub async fn get_vta_canonical_did_log_handler(
State(state): State<AppState>,
Path(did_log_path): Path<String>,
) -> Response {
let request_path = format!("/{}", did_log_path.trim_start_matches('/'));
if !request_path.ends_with("/did.jsonl") {
return StatusCode::NOT_FOUND.into_response();
}
serve_canonical(&state, &request_path).await
}
async fn serve_canonical(state: &AppState, request_path: &str) -> Response {
let Some((vta_did, expected_path)) = configured_canonical_path(state).await else {
return StatusCode::NOT_FOUND.into_response();
};
if request_path != expected_path {
return StatusCode::NOT_FOUND.into_response();
}
match crate::webvh_store::get_did_log(&state.webvh_ks, &vta_did).await {
Ok(Some(log)) => jsonl_response(log),
Ok(None) => StatusCode::NOT_FOUND.into_response(),
Err(e) => {
tracing::warn!(error = %e, "failed to read VTA did.jsonl from store");
StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
}
}
async fn configured_canonical_path(state: &AppState) -> Option<(String, String)> {
let vta_did = state.config.read().await.vta_did.clone()?;
if !vta_did.starts_with("did:webvh:") {
return None;
}
let parsed = WebVHURL::parse_did_url(&vta_did).ok()?;
let path = parsed.path.trim_end_matches('/');
Some((vta_did, format!("{path}/did.jsonl")))
}
fn jsonl_response(log: String) -> Response {
Response::builder()
.status(StatusCode::OK)
.header("content-type", JSONL_CONTENT_TYPE)
.header("x-content-type-options", "nosniff")
.body(Body::from(log))
.expect("static headers + owned body always build a valid response")
}
#[cfg(test)]
mod tests {
use axum::body::{Body, to_bytes};
use axum::http::{Request, StatusCode};
use tower::ServiceExt;
async fn get(
uri: &str,
vta_did: Option<&str>,
seed: Option<(&str, &str)>,
) -> (StatusCode, Option<String>, Vec<u8>) {
let (app, ctx) = crate::test_support::build_test_app().await;
ctx.config.write().await.vta_did = vta_did.map(str::to_string);
if let Some((did, log)) = seed {
crate::webvh_store::store_did_log(&ctx.webvh_ks, did, log)
.await
.expect("seed did log");
}
let req = Request::builder()
.uri(uri)
.method("GET")
.header("x-forwarded-for", "192.0.2.1")
.body(Body::empty())
.unwrap();
let resp = app.oneshot(req).await.unwrap();
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.map(|v| v.to_str().unwrap().to_string());
let body = to_bytes(resp.into_body(), usize::MAX)
.await
.unwrap()
.to_vec();
(status, content_type, body)
}
#[tokio::test]
async fn well_known_serves_root_webvh_log_with_spec_headers() {
let did = "did:webvh:QmSCID:example.com";
let log = r#"{"versionId":"1-abc","versionTime":"2025-01-01T00:00:00Z"}"#;
let (status, ct, body) = get("/.well-known/did.jsonl", Some(did), Some((did, log))).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(ct.as_deref(), Some("text/jsonl"));
assert_eq!(body, log.as_bytes());
}
#[tokio::test]
async fn well_known_404_for_pathful_did() {
let did = "did:webvh:QmSCID:example.com:tenant:vta";
let (status, _, body) = get("/.well-known/did.jsonl", Some(did), Some((did, "{}\n"))).await;
assert_eq!(status, StatusCode::NOT_FOUND);
assert!(body.is_empty(), "404 must be opaque (empty body)");
}
#[tokio::test]
async fn canonical_path_serves_pathful_log() {
let did = "did:webvh:QmSCID:example.com:tenant:vta";
let log = "{\"versionId\":\"1-abc\"}\n";
let (status, ct, body) = get("/tenant/vta/did.jsonl", Some(did), Some((did, log))).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(ct.as_deref(), Some("text/jsonl"));
assert_eq!(body, log.as_bytes());
}
#[tokio::test]
async fn catch_all_404_for_non_canonical_did_jsonl_path() {
let did = "did:webvh:QmSCID:example.com:tenant:vta";
let (status, _, body) = get("/wrong/path/did.jsonl", Some(did), Some((did, "{}\n"))).await;
assert_eq!(status, StatusCode::NOT_FOUND);
assert!(body.is_empty(), "non-canonical path must be opaque");
}
#[tokio::test]
async fn catch_all_unknown_path_is_bare_404() {
let did = "did:webvh:QmSCID:example.com";
let (status, _, body) = get("/totally/unknown/resource", Some(did), None).await;
assert_eq!(status, StatusCode::NOT_FOUND);
assert!(body.is_empty());
}
#[tokio::test]
async fn well_known_404_when_no_vta_did() {
let (status, _, body) = get("/.well-known/did.jsonl", None, None).await;
assert_eq!(status, StatusCode::NOT_FOUND);
assert!(body.is_empty(), "must not reveal that no DID is configured");
}
#[tokio::test]
async fn well_known_404_for_non_webvh_did() {
let (status, _, body) = get("/.well-known/did.jsonl", Some("did:key:z6Mkabc"), None).await;
assert_eq!(status, StatusCode::NOT_FOUND);
assert!(body.is_empty());
}
#[tokio::test]
async fn catch_all_does_not_shadow_authed_route() {
let (app, _ctx) = crate::test_support::build_test_app().await;
let req = Request::builder()
.uri("/keys")
.method("GET")
.body(Body::empty())
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert_ne!(
resp.status(),
StatusCode::NOT_FOUND,
"real route must not be shadowed by the did.jsonl catch-all"
);
}
}