diff --git a/agent_api_rest/src/holder/mod.rs b/agent_api_rest/src/holder/mod.rs index 1346bca..1db3200 100644 --- a/agent_api_rest/src/holder/mod.rs +++ b/agent_api_rest/src/holder/mod.rs @@ -34,7 +34,11 @@ pub fn router(holder_state: HolderState) -> Router { .route("/holder/offers/:offer_id/accept", post(accept)) .route("/holder/offers/:offer_id/reject", post(reject)), ) + // TODO: move behind UniCore API .route("/openid4vci/offers", post(openid4vci::offers)) + // TODO: move behind UniCore API + .route("/credential_offer", get(openid4vci::offers_params)) + .route("/", get(openid4vci::offers_params)) .route( "/linked-verifiable-presentations/:presentation_id", get(presentation_signed), diff --git a/agent_api_rest/src/holder/openid4vci/mod.rs b/agent_api_rest/src/holder/openid4vci/mod.rs index 18686d6..b1659ae 100644 --- a/agent_api_rest/src/holder/openid4vci/mod.rs +++ b/agent_api_rest/src/holder/openid4vci/mod.rs @@ -1,13 +1,14 @@ use agent_holder::{offer::command::OfferCommand, state::HolderState}; -use agent_shared::handlers::command_handler; +use agent_shared::handlers::{command_handler, query_handler}; use axum::{ extract::State, response::{IntoResponse, Response}, - Json, + Form, Json, }; use hyper::StatusCode; use oid4vci::credential_offer::CredentialOffer; use serde::{Deserialize, Serialize}; +use serde_json::Value; use tracing::info; #[derive(Deserialize, Serialize)] @@ -39,3 +40,55 @@ pub(crate) async fn offers(State(state): State, Json(payload): Json Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(), } } + +#[axum_macros::debug_handler] +pub(crate) async fn offers_params( + State(state): State, + Form(payload): Form, +) -> Response { + offers_inner(state, payload).await +} + +pub(crate) async fn offers_inner(state: HolderState, payload: serde_json::Value) -> Response { + info!("Request Body: {}", payload); + + let credential_offer_result: Result = + if let Some(credential_offer) = payload.get("credential_offer").and_then(Value::as_str) { + format!("openid-credential-offer://?credential_offer={credential_offer}") + } else if let Some(credential_offer_uri) = payload.get("credential_offer_uri").and_then(Value::as_str) { + format!("openid-credential-offer://?credential_offer_uri={credential_offer_uri}") + } else { + return (StatusCode::BAD_REQUEST, "invalid payload").into_response(); + } + .parse(); + + let credential_offer = match credential_offer_result { + Ok(credential_offer) => credential_offer, + Err(_) => return (StatusCode::BAD_REQUEST, "invalid payload").into_response(), + }; + + let received_offer_id = uuid::Uuid::new_v4().to_string(); + + info!("Credential Offer: {:#?}", credential_offer); + + let command = OfferCommand::ReceiveCredentialOffer { + received_offer_id: received_offer_id.clone(), + credential_offer, + }; + + // Add the Credential Offer to the state. + if command_handler(&received_offer_id, &state.command.offer, command) + .await + .is_err() + { + // TODO: add better Error responses. This needs to be done properly in all endpoints once + // https://github.com/impierce/openid4vc/issues/78 is fixed. + return StatusCode::INTERNAL_SERVER_ERROR.into_response(); + } + + match query_handler(&received_offer_id, &state.query.received_offer).await { + // TODO: add Location header + Ok(Some(received_offer)) => (StatusCode::CREATED, Json(received_offer)).into_response(), + _ => StatusCode::INTERNAL_SERVER_ERROR.into_response(), + } +} diff --git a/agent_issuance/src/offer/aggregate.rs b/agent_issuance/src/offer/aggregate.rs index 2bbe9d5..be1d7b8 100644 --- a/agent_issuance/src/offer/aggregate.rs +++ b/agent_issuance/src/offer/aggregate.rs @@ -114,9 +114,19 @@ impl Aggregate for Offer { // TODO: add to `service`? let client = reqwest::Client::new(); + let form_url_encoded_credential_offer = self + .credential_offer + .as_ref() + .ok_or(MissingCredentialOfferError)? + .to_string(); + + let target = + form_url_encoded_credential_offer.replace("openid-credential-offer://", target_url.as_str()); + + info!("Sending credential offer to: {}", target); + client - .post(target_url.clone()) - .json(self.credential_offer.as_ref().ok_or(MissingCredentialOfferError)?) + .get(target) .send() .await .map_err(|e| SendCredentialOfferError(e.to_string()))?;