Skip to content

Commit

Permalink
return application json encoded authorization response (#39)
Browse files Browse the repository at this point in the history
* use serde deserialize serialize derive for authorization response

Signed-off-by: Ryan Tate <[email protected]>

* update url encoding for auth response

Signed-off-by: Ryan Tate <[email protected]>

* fix unit tests

Signed-off-by: Ryan Tate <[email protected]>

* use custom struct for json string encoded authorization response inner values

Signed-off-by: Ryan Tate <[email protected]>

---------

Signed-off-by: Ryan Tate <[email protected]>
  • Loading branch information
Ryanmtate authored Nov 4, 2024
1 parent ab9a643 commit d4834ac
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 59 deletions.
17 changes: 0 additions & 17 deletions src/core/object/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::collections::BTreeMap;

use anyhow::{Context, Error, Result};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value as Json};
Expand Down Expand Up @@ -61,21 +59,6 @@ impl UntypedObject {
),
}
}

/// Flatten the structure for posting as a form.
pub(crate) fn flatten_for_form(self) -> Result<BTreeMap<String, String>> {
self.0
.into_iter()
.map(|(k, v)| {
if let Json::String(s) = v {
return Ok((k, s));
}
serde_json::to_string(&v)
.map(|v| (k, v))
.map_err(Error::from)
})
.collect()
}
}

impl From<UntypedObject> for Json {
Expand Down
93 changes: 56 additions & 37 deletions src/core/response/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
use super::{
object::{ParsingErrorContext, UntypedObject},
presentation_submission::PresentationSubmission,
};

use std::collections::BTreeMap;
use super::{object::UntypedObject, presentation_submission::PresentationSubmission};

use anyhow::{Context, Error, Result};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use url::Url;

use self::parameters::VpToken;
Expand All @@ -26,45 +20,72 @@ impl AuthorizationResponse {
return Ok(Self::Jwt(jwt));
}

let flattened = serde_urlencoded::from_bytes::<BTreeMap<String, String>>(bytes)
let unencoded = serde_urlencoded::from_bytes::<JsonEncodedAuthorizationResponse>(bytes)
.context("failed to construct flat map")?;
let map = flattened
.into_iter()
.map(|(k, v)| {
let v = serde_json::from_str::<Value>(&v).unwrap_or(Value::String(v));
(k, v)
})
.collect();

Ok(Self::Unencoded(UntypedObject(map).try_into()?))

let vp_token: VpToken =
serde_json::from_str(&unencoded.vp_token).context("failed to decode vp token")?;

let presentation_submission: PresentationSubmission =
serde_json::from_str(&unencoded.presentation_submission)
.context("failed to decode presentation submission")?;

Ok(Self::Unencoded(UnencodedAuthorizationResponse {
vp_token,
presentation_submission,
}))
}
}

#[derive(Debug, Clone)]
pub struct UnencodedAuthorizationResponse(
pub UntypedObject,
pub VpToken,
pub PresentationSubmission,
);
#[derive(Debug, Deserialize, Serialize)]
struct JsonEncodedAuthorizationResponse {
/// `vp_token` is JSON string encoded.
pub(crate) vp_token: String,
/// `presentation_submission` is JSON string encoded.
pub(crate) presentation_submission: String,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UnencodedAuthorizationResponse {
pub vp_token: VpToken,
pub presentation_submission: PresentationSubmission,
}

impl UnencodedAuthorizationResponse {
/// Encode the Authorization Response as 'application/x-www-form-urlencoded'.
pub fn into_x_www_form_urlencoded(self) -> Result<String> {
let mut inner = self.0;
inner.insert(self.1);
inner.insert(self.2);
serde_urlencoded::to_string(inner.flatten_for_form()?)
.context("failed to encode response as 'application/x-www-form-urlencoded'")
let encoded = serde_urlencoded::to_string(JsonEncodedAuthorizationResponse::from(self))
.context(
"failed to encode presentation_submission as 'application/x-www-form-urlencoded'",
)?;

Ok(encoded)
}

/// Return the Verifiable Presentation Token.
pub fn vp_token(&self) -> &VpToken {
&self.1
&self.vp_token
}

/// Return the Presentation Submission.
pub fn presentation_submission(&self) -> &PresentationSubmission {
&self.2
&self.presentation_submission
}
}

impl From<UnencodedAuthorizationResponse> for JsonEncodedAuthorizationResponse {
fn from(value: UnencodedAuthorizationResponse) -> Self {
let vp_token = serde_json::to_string(&value.vp_token)
// SAFTEY: VP Token will always be a valid JSON object.
.unwrap();
let presentation_submission = serde_json::to_string(&value.presentation_submission)
// SAFETY: presentation submission will always be a valid JSON object.
.unwrap();

Self {
vp_token,
presentation_submission,
}
}
}

Expand All @@ -91,9 +112,7 @@ impl TryFrom<UntypedObject> for UnencodedAuthorizationResponse {
type Error = Error;

fn try_from(value: UntypedObject) -> Result<Self, Self::Error> {
let vp_token = value.get().parsing_error()?;
let presentation_submission = value.get().parsing_error()?;
Ok(Self(value, vp_token, presentation_submission))
Ok(serde_json::from_value(serde_json::Value::Object(value.0))?)
}
}

Expand Down Expand Up @@ -130,9 +149,9 @@ mod test {
))
.unwrap();
let response = UnencodedAuthorizationResponse::try_from(object).unwrap();
assert_eq!(
response.into_x_www_form_urlencoded().unwrap(),
"presentation_submission=%7B%22id%22%3A%22d05a7f51-ac09-43af-8864-e00f0175f2c7%22%2C%22definition_id%22%3A%22f619e64a-8f80-4b71-8373-30cf07b1e4f2%22%2C%22descriptor_map%22%3A%5B%5D%7D&vp_token=string",
)
let url_encoded = response.into_x_www_form_urlencoded().unwrap();

assert!(url_encoded.contains("presentation_submission=%7B%22id%22%3A%22d05a7f51-ac09-43af-8864-e00f0175f2c7%22%2C%22definition_id%22%3A%22f619e64a-8f80-4b71-8373-30cf07b1e4f2%22%2C%22descriptor_map%22%3A%5B%5D%7D"));
assert!(url_encoded.contains("vp_token=%22string%22"));
}
}
9 changes: 4 additions & 5 deletions tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,10 @@ async fn w3c_vc_did_client_direct_post() {
.await
.expect("failed to create verifiable presentation");

let response = AuthorizationResponse::Unencoded(UnencodedAuthorizationResponse(
Default::default(),
vp.into(),
presentation_submission.try_into().unwrap(),
));
let response = AuthorizationResponse::Unencoded(UnencodedAuthorizationResponse {
vp_token: vp.into(),
presentation_submission: presentation_submission.try_into().unwrap(),
});

let status = verifier.poll_status(id).await.unwrap();
assert_eq!(Status::SentRequest, status);
Expand Down

0 comments on commit d4834ac

Please sign in to comment.