use chrono::Utc;
use serde::{Deserialize, Serialize};
use trust_tasks_rs::{
handlers::InMemoryHandler, ErrorPayload, ErrorResponse, RejectReason, StandardCode,
TransportHandler, TrustTask, TypeUri,
};
#[derive(Debug, Serialize, Deserialize)]
struct KycHandoff {
subject: String,
result: String,
level: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct KycReceipt {
receipt_id: String,
accepted_at: chrono::DateTime<Utc>,
}
const VERIFIER: &str = "did:web:verifier.example";
const BANK: &str = "did:web:bank.example";
#[allow(clippy::result_large_err)]
fn handle(
request: TrustTask<KycHandoff>,
handler: &impl TransportHandler,
) -> Result<TrustTask<KycReceipt>, ErrorResponse> {
let new_id = |stem: &str| format!("urn:example:{}-{}", stem, request.id);
let _resolved = handler
.resolve_parties(&request)
.map_err(|e| request.reject_with(new_id("err"), RejectReason::from(e)))?;
request
.validate_basic(Utc::now(), BANK)
.map_err(|reason| request.reject_with(new_id("err"), reason))?;
if request.payload.result != "passed" {
return Err(request.reject_with(
new_id("err"),
ErrorPayload::new(StandardCode::TaskFailed).with_message("KYC result was not 'passed'"),
));
}
Ok(request.respond_with(
new_id("resp"),
KycReceipt {
receipt_id: format!("rcpt-{}", request.id),
accepted_at: Utc::now(),
},
))
}
fn main() {
let producer = InMemoryHandler::new().with_local(VERIFIER).with_peer(BANK);
let consumer = InMemoryHandler::new().with_local(BANK).with_peer(VERIFIER);
let mut request = TrustTask::new(
"req-001",
TypeUri::canonical("kyc-handoff", 1, 0).unwrap(),
KycHandoff {
subject: "did:key:z6Mk...".into(),
result: "passed".into(),
level: "LOA2".into(),
},
);
request.issuer = Some(VERIFIER.into());
request.recipient = Some(BANK.into());
request.issued_at = Some(Utc::now());
producer.prepare_outbound(&mut request);
println!(
"REQUEST (wire form, in-band parties stripped since transport conveys them):\n{}",
serde_json::to_string_pretty(&request).unwrap()
);
match handle(request, &consumer) {
Ok(response) => println!(
"\nSUCCESS RESPONSE:\n{}",
serde_json::to_string_pretty(&response).unwrap()
),
Err(err) => println!(
"\nERROR RESPONSE:\n{}",
serde_json::to_string_pretty(&err).unwrap()
),
}
let mut tampered = TrustTask::new(
"req-002",
TypeUri::canonical("kyc-handoff", 1, 0).unwrap(),
KycHandoff {
subject: "did:key:z6Mk...".into(),
result: "passed".into(),
level: "LOA2".into(),
},
);
tampered.issuer = Some("did:web:attacker.example".into());
match handle(tampered, &consumer) {
Ok(_) => unreachable!("identity mismatch must be rejected"),
Err(err) => println!(
"\nERROR RESPONSE (identity mismatch):\n{}",
serde_json::to_string_pretty(&err).unwrap()
),
}
}