use crate::auth_di;
use crate::session::TokenResult;
use crate::trust_tasks::TASK_AUTH_AUTHENTICATE_0_2;
pub async fn challenge_response_di(
base_url: &str,
client_did: &str,
private_key_multibase: &str,
vta_did: &str,
) -> Result<TokenResult, Box<dyn std::error::Error>> {
let http = crate::http::rest_client();
let trust_tasks_url = format!("{base_url}/trust-tasks");
let challenge_body = auth_di::build_challenge_doc(client_did, vta_did, client_did)?;
let challenge_resp = http
.post(&trust_tasks_url)
.header("content-type", "application/json")
.header("Trust-Task", crate::trust_tasks::TASK_AUTH_CHALLENGE_0_1)
.body(challenge_body)
.send()
.await
.map_err(|e| format!("could not connect to VTA at {trust_tasks_url}: {e}"))?;
if !challenge_resp.status().is_success() {
let status = challenge_resp.status();
let headers = challenge_resp.headers().clone();
let body = challenge_resp.text().await.unwrap_or_default();
if let Some(e) = crate::error::VtaError::rate_limited_from_http(
status,
&headers,
&body,
&trust_tasks_url,
) {
return Err(e.into());
}
return Err(format!("challenge request failed ({status}): {body}").into());
}
let challenge_text = challenge_resp
.text()
.await
.map_err(|e| format!("failed to read challenge response from VTA: {e}"))?;
let challenge = auth_di::parse_challenge_response(&challenge_text).map_err(|e| {
format!("unexpected challenge response from VTA at {trust_tasks_url} (is this a VTA?): {e}")
})?;
let body = auth_di::sign_authenticate_doc(
client_did,
private_key_multibase,
vta_did,
&challenge.challenge,
&challenge.session_id,
)
.await?;
let auth_resp = http
.post(&trust_tasks_url)
.header("content-type", "application/json")
.header("Trust-Task", TASK_AUTH_AUTHENTICATE_0_2)
.body(body)
.send()
.await
.map_err(|e| format!("could not connect to VTA at {trust_tasks_url}: {e}"))?;
let status = auth_resp.status();
if !status.is_success() {
let headers = auth_resp.headers().clone();
let body = auth_resp.text().await.unwrap_or_default();
if let Some(e) = crate::error::VtaError::rate_limited_from_http(
status,
&headers,
&body,
&trust_tasks_url,
) {
return Err(e.into());
}
return Err(format!("authentication failed ({status}): {body}").into());
}
let auth_text = auth_resp
.text()
.await
.map_err(|e| format!("failed to read auth response from VTA: {e}"))?;
let auth_data = auth_di::parse_auth_response(&auth_text)
.map_err(|e| format!("{e} (VTA at {trust_tasks_url})"))?;
let access_expires_at = auth_data.access_expires_at_epoch().ok_or_else(|| {
format!(
"VTA returned unparseable session.issuedAt: '{}'",
auth_data.session.issued_at
)
})?;
Ok(TokenResult {
access_token: auth_data.tokens.access_token,
access_expires_at,
})
}