diff --git a/src/core/authorization_request/mod.rs b/src/core/authorization_request/mod.rs index 68f4cf8..4bdfbc4 100644 --- a/src/core/authorization_request/mod.rs +++ b/src/core/authorization_request/mod.rs @@ -1,7 +1,7 @@ use std::ops::{Deref, DerefMut}; use anyhow::{anyhow, bail, Context, Error, Result}; -use parameters::ClientMetadata; +use parameters::{ClientMetadata, State}; use serde::{Deserialize, Serialize}; use serde_json::Value as Json; use url::Url; @@ -280,6 +280,12 @@ impl AuthorizationRequestObject { .get() .ok_or(anyhow!("missing vp_formats"))? } + + /// Return the `state` of the authorization request, + /// if it was provided. + pub fn state(&self) -> Option> { + self.0.get() + } } impl From for UntypedObject { diff --git a/src/core/authorization_request/parameters.rs b/src/core/authorization_request/parameters.rs index a733156..502e832 100644 --- a/src/core/authorization_request/parameters.rs +++ b/src/core/authorization_request/parameters.rs @@ -576,7 +576,7 @@ impl From for Json { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, Serialize)] pub struct State(pub String); impl TypedParameter for State { diff --git a/src/core/presentation_submission.rs b/src/core/presentation_submission.rs index 73a87c5..2273ff0 100644 --- a/src/core/presentation_submission.rs +++ b/src/core/presentation_submission.rs @@ -194,6 +194,7 @@ pub struct DescriptorMap { pub id: DescriptorMapId, pub format: ClaimFormatDesignation, pub path: JsonPath, + #[serde(skip_serializing_if = "Option::is_none")] pub path_nested: Option>, } diff --git a/src/core/response/mod.rs b/src/core/response/mod.rs index b4a8c92..0a709d8 100644 --- a/src/core/response/mod.rs +++ b/src/core/response/mod.rs @@ -1,6 +1,7 @@ use super::{object::UntypedObject, presentation_submission::PresentationSubmission}; use anyhow::{Context, Error, Result}; +use parameters::State; use serde::{Deserialize, Serialize}; use url::Url; @@ -8,7 +9,8 @@ use self::parameters::VpToken; pub mod parameters; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] pub enum AuthorizationResponse { Unencoded(UnencodedAuthorizationResponse), Jwt(JwtAuthorizationResponse), @@ -30,25 +32,37 @@ impl AuthorizationResponse { serde_json::from_str(&unencoded.presentation_submission) .context("failed to decode presentation submission")?; + let state: Option = unencoded + .state + .map(|s| serde_json::from_str(&s)) + .transpose() + .context("failed to decode state")?; + Ok(Self::Unencoded(UnencodedAuthorizationResponse { vp_token, presentation_submission, + state, })) } } #[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, + /// `vp_token` is JSON string encoded. + pub(crate) vp_token: String, + /// `state` is a regular string. + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) state: Option, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct UnencodedAuthorizationResponse { - pub vp_token: VpToken, pub presentation_submission: PresentationSubmission, + pub vp_token: VpToken, + #[serde(skip_serializing_if = "Option::is_none")] + pub state: Option, } impl UnencodedAuthorizationResponse { @@ -73,18 +87,37 @@ impl UnencodedAuthorizationResponse { } } +// Helper method for cleaning up quoted strings. +// urlencoding a JSON string adds quotes around the string, +// which causes issues when decoding. +fn clean_quoted_string(s: &str) -> String { + s.trim_matches(|c| c == '"' || c == '\'').to_string() +} + impl From for JsonEncodedAuthorizationResponse { fn from(value: UnencodedAuthorizationResponse) -> Self { let vp_token = serde_json::to_string(&value.vp_token) + .ok() + // Need to strip quotes from the string. + .map(|s| clean_quoted_string(&s)) // 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(); + let state = value + .state + .map(|s| serde_json::to_string(&s)) + .transpose() + // SAFETY: State will always be a valid JSON object. + .unwrap(); + Self { vp_token, presentation_submission, + state, } } } diff --git a/tests/e2e.rs b/tests/e2e.rs index e486f8e..f698145 100644 --- a/tests/e2e.rs +++ b/tests/e2e.rs @@ -143,6 +143,7 @@ async fn w3c_vc_did_client_direct_post() { let response = AuthorizationResponse::Unencoded(UnencodedAuthorizationResponse { vp_token: vp.into(), presentation_submission: presentation_submission.try_into().unwrap(), + state: None, }); let status = verifier.poll_status(id).await.unwrap();