Skip to content

Commit be27695

Browse files
refactors .well-known/ts.jwks.json -> .well-known/trusted-server.json
1 parent 68ebba4 commit be27695

File tree

4 files changed

+184
-6
lines changed

4 files changed

+184
-6
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//! Discovery endpoint for trusted-server.
2+
//!
3+
//! This module provides a standardized discovery mechanism similar to the IAB's
4+
//! Data Subject Rights framework. The `.well-known/trusted-server.json` endpoint
5+
//! allows partners to discover JWKS keys and API endpoints.
6+
7+
use serde::Serialize;
8+
9+
/// Main discovery document returned by `.well-known/trusted-server.json`
10+
#[derive(Debug, Serialize)]
11+
pub struct TrustedServerDiscovery {
12+
/// Version of the discovery document format
13+
pub version: String,
14+
15+
/// JSON Web Key Set containing public keys for signature verification
16+
pub jwks: serde_json::Value,
17+
18+
/// Available API endpoints
19+
pub endpoints: DiscoveryEndpoints,
20+
}
21+
22+
/// API endpoints available on this trusted-server instance
23+
#[derive(Debug, Serialize)]
24+
pub struct DiscoveryEndpoints {
25+
/// Endpoint for signing first-party requests
26+
pub sign: String,
27+
28+
/// Endpoint for verifying signatures
29+
pub verify: String,
30+
31+
/// Admin endpoint for key rotation
32+
pub rotate_key: String,
33+
34+
/// Admin endpoint for key deactivation
35+
pub deactivate_key: String,
36+
}
37+
38+
impl TrustedServerDiscovery {
39+
/// Creates a new discovery document with the given JWKS
40+
pub fn new(jwks_value: serde_json::Value) -> Self {
41+
Self {
42+
version: "1.0".to_string(),
43+
jwks: jwks_value,
44+
endpoints: DiscoveryEndpoints {
45+
sign: "/first-party/sign".to_string(),
46+
verify: "/verify-signature".to_string(),
47+
rotate_key: "/admin/keys/rotate".to_string(),
48+
deactivate_key: "/admin/keys/deactivate".to_string(),
49+
},
50+
}
51+
}
52+
}
53+
54+
#[cfg(test)]
55+
mod tests {
56+
use super::*;
57+
use serde_json::json;
58+
59+
#[test]
60+
fn test_discovery_document_structure() {
61+
let jwks = json!({
62+
"keys": [
63+
{
64+
"kty": "OKP",
65+
"crv": "Ed25519",
66+
"x": "test_key",
67+
"kid": "test-kid"
68+
}
69+
]
70+
});
71+
72+
let discovery = TrustedServerDiscovery::new(jwks);
73+
74+
assert_eq!(discovery.version, "1.0");
75+
assert_eq!(discovery.endpoints.sign, "/first-party/sign");
76+
assert_eq!(discovery.endpoints.verify, "/verify-signature");
77+
assert_eq!(discovery.endpoints.rotate_key, "/admin/keys/rotate");
78+
assert_eq!(discovery.endpoints.deactivate_key, "/admin/keys/deactivate");
79+
}
80+
81+
#[test]
82+
fn test_discovery_document_serialization() {
83+
let jwks = json!({
84+
"keys": []
85+
});
86+
87+
let discovery = TrustedServerDiscovery::new(jwks);
88+
let serialized = serde_json::to_string(&discovery).unwrap();
89+
90+
// Verify it's valid JSON
91+
let parsed: serde_json::Value = serde_json::from_str(&serialized).unwrap();
92+
93+
assert_eq!(parsed["version"], "1.0");
94+
assert_eq!(parsed["endpoints"]["sign"], "/first-party/sign");
95+
assert!(parsed.get("jwks").is_some());
96+
}
97+
98+
#[test]
99+
fn test_discovery_includes_jwks() {
100+
let jwks = json!({
101+
"keys": [
102+
{
103+
"kty": "OKP",
104+
"kid": "test-key"
105+
}
106+
]
107+
});
108+
109+
let discovery = TrustedServerDiscovery::new(jwks);
110+
let serialized = serde_json::to_string(&discovery).unwrap();
111+
let parsed: serde_json::Value = serde_json::from_str(&serialized).unwrap();
112+
113+
assert!(parsed["jwks"]["keys"].is_array());
114+
assert_eq!(parsed["jwks"]["keys"][0]["kid"], "test-key");
115+
}
116+
}

crates/common/src/request_signing/endpoints.rs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,43 @@ use fastly::{Request, Response};
88
use serde::{Deserialize, Serialize};
99

1010
use crate::error::TrustedServerError;
11+
use crate::request_signing::discovery::TrustedServerDiscovery;
1112
use crate::request_signing::rotation::KeyRotationManager;
1213
use crate::request_signing::signing;
1314
use crate::settings::Settings;
1415

15-
/// Retrieves and returns active jwks public keys.
16-
pub fn handle_jwks_endpoint(
16+
/// Retrieves and returns the trusted-server discovery document.
17+
///
18+
/// This endpoint provides a standardized discovery mechanism following the IAB
19+
/// Data Subject Rights framework pattern. It returns JWKS keys and API endpoints
20+
/// in a single discoverable location.
21+
pub fn handle_trusted_server_discovery(
1722
_settings: &Settings,
1823
_req: Request,
1924
) -> Result<Response, Report<TrustedServerError>> {
25+
// Get JWKS
2026
let jwks_json = crate::request_signing::jwks::get_active_jwks().change_context(
2127
TrustedServerError::Configuration {
2228
message: "Failed to retrieve JWKS".into(),
2329
},
2430
)?;
2531

32+
let jwks_value: serde_json::Value =
33+
serde_json::from_str(&jwks_json).change_context(TrustedServerError::Configuration {
34+
message: "Failed to parse JWKS JSON".into(),
35+
})?;
36+
37+
let discovery = TrustedServerDiscovery::new(jwks_value);
38+
39+
let json = serde_json::to_string_pretty(&discovery).change_context(
40+
TrustedServerError::Configuration {
41+
message: "Failed to serialize discovery document".into(),
42+
},
43+
)?;
44+
2645
Ok(Response::from_status(200)
2746
.with_content_type(fastly::mime::APPLICATION_JSON)
28-
.with_body_text_plain(&jwks_json))
47+
.with_body_text_plain(&json))
2948
}
3049

3150
#[derive(Debug, Deserialize, Serialize)]
@@ -514,4 +533,42 @@ mod tests {
514533
assert_eq!(req.kid, "old-key");
515534
assert!(req.delete);
516535
}
536+
537+
#[test]
538+
fn test_handle_trusted_server_discovery() {
539+
let settings = crate::test_support::tests::create_test_settings();
540+
let req = Request::new(
541+
Method::GET,
542+
"https://test.com/.well-known/trusted-server.json",
543+
);
544+
545+
let result = handle_trusted_server_discovery(&settings, req);
546+
match result {
547+
Ok(mut resp) => {
548+
assert_eq!(resp.get_status(), StatusCode::OK);
549+
let body = resp.take_body_str();
550+
551+
// Parse the discovery document
552+
let discovery: serde_json::Value = serde_json::from_str(&body).unwrap();
553+
554+
// Verify structure
555+
assert_eq!(discovery["version"], "1.0");
556+
assert!(discovery["jwks"].is_object());
557+
assert!(discovery["endpoints"].is_object());
558+
559+
// Verify endpoints
560+
assert_eq!(discovery["endpoints"]["sign"], "/first-party/sign");
561+
assert_eq!(discovery["endpoints"]["verify"], "/verify-signature");
562+
assert_eq!(discovery["endpoints"]["rotate_key"], "/admin/keys/rotate");
563+
assert_eq!(
564+
discovery["endpoints"]["deactivate_key"],
565+
"/admin/keys/deactivate"
566+
);
567+
568+
// Verify no capabilities field
569+
assert!(discovery.get("capabilities").is_none());
570+
}
571+
Err(e) => println!("Expected error in test environment: {}", e),
572+
}
573+
}
517574
}

crates/common/src/request_signing/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
//! This module provides cryptographic signing capabilities using Ed25519 keys,
44
//! including JWKS management, key rotation, and signature verification.
55
6+
pub mod discovery;
67
pub mod endpoints;
78
pub mod jwks;
89
pub mod rotation;
910
pub mod signing;
1011

12+
pub use discovery::*;
1113
pub use endpoints::*;
1214
pub use jwks::*;
1315
pub use rotation::*;

crates/fastly/src/main.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use trusted_server_common::proxy::{
1212
};
1313
use trusted_server_common::publisher::{handle_publisher_request, handle_tsjs_dynamic};
1414
use trusted_server_common::request_signing::{
15-
handle_deactivate_key, handle_jwks_endpoint, handle_rotate_key, handle_verify_signature,
15+
handle_deactivate_key, handle_rotate_key, handle_trusted_server_discovery,
16+
handle_verify_signature,
1617
};
1718
use trusted_server_common::settings::Settings;
1819
use trusted_server_common::settings_data::get_settings;
@@ -62,8 +63,10 @@ async fn route_request(
6263
handle_tsjs_dynamic(&settings, req)
6364
}
6465

65-
// JWKS endpoint for public key distribution
66-
(Method::GET, "/.well-known/ts.jwks.json") => handle_jwks_endpoint(&settings, req),
66+
// Discovery endpoint for trusted-server capabilities and JWKS
67+
(Method::GET, "/.well-known/trusted-server.json") => {
68+
handle_trusted_server_discovery(&settings, req)
69+
}
6770

6871
// Signature verification endpoint
6972
(Method::POST, "/verify-signature") => handle_verify_signature(&settings, req),

0 commit comments

Comments
 (0)