Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ Behavior is covered by an extensive test suite in `crates/common/src/creative.rs

- `synthetic.rs` generates a deterministic synthetic identifier per user request and exposes helpers:
- `generate_synthetic_id` — creates a fresh HMAC-based ID using request signals.
- `get_synthetic_id` — extracts an existing ID from the `X-Synthetic-Trusted-Server` header or `synthetic_id` cookie.
- `get_synthetic_id` — extracts an existing ID from the `x-psid-ts` header or `synthetic_id` cookie.
- `get_or_generate_synthetic_id` — reuses the existing ID when present, otherwise creates one.
- `publisher.rs::handle_publisher_request` stamps proxied origin responses with `X-Synthetic-Fresh`, `X-Synthetic-Trusted-Server`, and (when absent) issues the `synthetic_id` cookie so the browser keeps the identifier on subsequent requests.
- `publisher.rs::handle_publisher_request` stamps proxied origin responses with `X-Synthetic-Fresh`, `x-psid-ts`, and (when absent) issues the `synthetic_id` cookie so the browser keeps the identifier on subsequent requests.
- `proxy.rs::handle_first_party_proxy` replays the identifier to third-party creative origins by appending `synthetic_id=<value>` to the reconstructed target URL, follows redirects (301/302/303/307/308) up to four hops, and keeps downstream fetches linked to the same user scope.
- `proxy.rs::handle_first_party_click` adds `synthetic_id=<value>` to outbound click redirect URLs so analytics endpoints can associate clicks with impressions without third-party cookies.
2 changes: 1 addition & 1 deletion crates/common/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use http::header::HeaderName;

pub const HEADER_SYNTHETIC_FRESH: HeaderName = HeaderName::from_static("x-synthetic-fresh");

Check warning on line 3 in crates/common/src/constants.rs

View workflow job for this annotation

GitHub Actions / cargo fmt

Diff in /home/runner/work/trusted-server/trusted-server/crates/common/src/constants.rs
pub const HEADER_SYNTHETIC_PUB_USER_ID: HeaderName = HeaderName::from_static("x-pub-user-id");
pub const HEADER_X_PUB_USER_ID: HeaderName = HeaderName::from_static("x-pub-user-id");
pub const HEADER_SYNTHETIC_TRUSTED_SERVER: HeaderName =
HeaderName::from_static("x-synthetic-trusted-server");
HeaderName::from_static("x-psid-ts");
pub const HEADER_X_CONSENT_ADVERTISING: HeaderName =
HeaderName::from_static("x-consent-advertising");
pub const HEADER_X_FORWARDED_FOR: HeaderName = HeaderName::from_static("x-forwarded-for");
Expand Down
11 changes: 6 additions & 5 deletions crates/common/src/gam.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use std::io::Read;
use uuid::Uuid;

use crate::constants::HEADER_SYNTHETIC_TRUSTED_SERVER;
use crate::error::TrustedServerError;
use crate::gdpr::get_consent_from_request;
use crate::settings::Settings;
Expand Down Expand Up @@ -35,7 +36,7 @@

// Get synthetic ID from request headers
let synthetic_id = req
.get_header("X-Synthetic-Trusted-Server")
.get_header(HEADER_SYNTHETIC_TRUSTED_SERVER)
.and_then(|h| h.to_str().ok())
.unwrap_or("unknown")
.to_string();
Expand Down Expand Up @@ -1074,15 +1075,15 @@

#[cfg(test)]
mod tests {
use super::*;

Check warning on line 1078 in crates/common/src/gam.rs

View workflow job for this annotation

GitHub Actions / cargo fmt

Diff in /home/runner/work/trusted-server/trusted-server/crates/common/src/gam.rs
use serde_json::json;

use crate::test_support::tests::create_test_settings;
use crate::{constants::HEADER_SYNTHETIC_TRUSTED_SERVER, test_support::tests::create_test_settings};

fn create_test_request() -> Request {
let mut req = Request::new(Method::GET, "https://example.com/test");
req.set_header(header::USER_AGENT, "Mozilla/5.0 Test Browser");
req.set_header("X-Synthetic-Trusted-Server", "test-synthetic-id-123");
req.set_header(HEADER_SYNTHETIC_TRUSTED_SERVER, "test-synthetic-id-123");
req
}

Expand Down Expand Up @@ -1171,7 +1172,7 @@
Method::GET,
"https://example.com/test?param=value&special=test%20space",
);
req.set_header("X-Synthetic-Trusted-Server", "test-id");
req.set_header(HEADER_SYNTHETIC_TRUSTED_SERVER, "test-id");

let gam_req = GamRequest::new(&settings, &req).unwrap();
let url = gam_req.build_golden_url();
Expand Down Expand Up @@ -1215,7 +1216,7 @@
let settings = create_test_settings();
let mut req = Request::new(Method::GET, "https://example.com/gam-test");
req.set_header("X-Consent-Advertising", "true");
req.set_header("X-Synthetic-Trusted-Server", "test-synthetic-id");
req.set_header(HEADER_SYNTHETIC_TRUSTED_SERVER, "test-synthetic-id");

// Note: This test will fail when actually sending to GAM backend
// In a real test environment, we'd mock the backend response
Expand Down
2 changes: 1 addition & 1 deletion crates/common/src/publisher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ pub fn handle_main_page(
let fresh_id = generate_synthetic_id(settings, &req)?;

// Check for existing Trusted Server ID in this specific order:
// 1. X-Synthetic-Trusted-Server header
// 1. x-psid-ts header
// 2. Cookie
// 3. Fall back to fresh ID
let synthetic_id = get_or_generate_synthetic_id(settings, &req)?;
Expand Down
2 changes: 1 addition & 1 deletion crates/common/src/synthetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub fn generate_synthetic_id(
/// Gets or creates a synthetic ID from the request.
///
/// Attempts to retrieve an existing synthetic ID from:
/// 1. The `X-Synthetic-Trusted-Server` header
/// 1. The `x-psid-ts` header
/// 2. The `synthetic_id` cookie
///
/// If neither exists, generates a new synthetic ID.
Expand Down
6 changes: 3 additions & 3 deletions crates/common/src/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1208,7 +1208,7 @@ pub const GAM_TEST_TEMPLATE: &str = r#"
try {
const response = await fetch('/');
const freshId = response.headers.get('X-Synthetic-Fresh');
const trustedServerId = response.headers.get('X-Synthetic-Trusted-Server');
const trustedServerId = response.headers.get('x-psid-ts');

const statusDiv = document.getElementById('syntheticStatus');
statusDiv.className = 'status success';
Expand Down Expand Up @@ -1280,14 +1280,14 @@ pub const GAM_TEST_TEMPLATE: &str = r#"
// First get the main page to ensure we have synthetic IDs
const mainResponse = await fetch('/');
const freshId = mainResponse.headers.get('X-Synthetic-Fresh');
const trustedServerId = mainResponse.headers.get('X-Synthetic-Trusted-Server');
const trustedServerId = mainResponse.headers.get('x-psid-ts');

// Now test the GAM request
const response = await fetch('/gam-test', {
headers: {
'X-Consent-Advertising': 'true',
'X-Synthetic-Fresh': freshId || '',
'X-Synthetic-Trusted-Server': trustedServerId || ''
'x-psid-ts': trustedServerId || ''
}
});

Expand Down
2 changes: 1 addition & 1 deletion crates/js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ The Rust services (`trusted-server-common`) expose several proxy entry points th

- Endpoint: `handle_publisher_request` (`crates/common/src/publisher.rs`).
- Retrieves or generates the trusted synthetic identifier before Fastly consumes the request body.
- Always stamps the proxied response with `X-Synthetic-Fresh` and `X-Synthetic-Trusted-Server` headers and, when the browser does not already present one, sets the `synthetic_id=<value>` cookie (Secure + SameSite=Lax) bound to the configured publisher domain.
- Always stamps the proxied response with `X-Synthetic-Fresh` and `x-psid-ts` headers and, when the browser does not already present one, sets the `synthetic_id=<value>` cookie (Secure + SameSite=Lax) bound to the configured publisher domain.
- Result: downstream assets fetched through the same first-party origin automatically include the synthetic ID header/cookie so subsequent proxy layers can read it.

### Creative Asset Proxy
Expand Down
Loading