From 7318755f9987bec257f41926f147eaacf116360f Mon Sep 17 00:00:00 2001 From: Ryan Tate Date: Wed, 30 Oct 2024 10:09:02 -0700 Subject: [PATCH] add missing typed parameters for client metadata --- Cargo.toml | 4 +- src/core/authorization_request/mod.rs | 17 +++ src/core/authorization_request/parameters.rs | 138 +++++++++++++++++- .../authorization_request/verification/did.rs | 8 + src/core/credential_format/mod.rs | 1 + src/core/input_descriptor.rs | 5 + src/core/metadata/mod.rs | 31 +++- src/core/metadata/parameters/verifier.rs | 31 +++- src/core/metadata/parameters/wallet.rs | 69 ++++++++- src/core/object/mod.rs | 17 --- src/core/response/mod.rs | 93 +++++++----- src/core/response/parameters.rs | 56 ++++++- tests/e2e.rs | 9 +- 13 files changed, 408 insertions(+), 71 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index aeed68d..f84ec68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ http = "1.1.0" # There may be a better way to handle this that doesn't require the `json-syntax` crate directly. json-syntax = { version = "0.12.5", features = ["serde_json"] } # jsonpath_lib = "0.3.0" -serde_json_path = "0.6.7" +serde_json_path = "0.7.1" jsonschema = "0.18.0" openid4vp-frontend = { version = "0.1.0", path = "openid4vp-frontend" } p256 = { version = "0.13.2", features = ["jwk"] } @@ -30,7 +30,7 @@ reqwest = { version = "0.12.5", features = ["rustls-tls"] } serde = "1.0.188" serde_json = "1.0.107" serde_urlencoded = "0.7.1" -ssi = { version = "0.10", features = ["secp256r1"] } +ssi = { version = "0.10.1", features = ["secp256r1"] } tokio = "1.32.0" tracing = "0.1.37" url = { version = "2.4.1", features = ["serde"] } diff --git a/src/core/authorization_request/mod.rs b/src/core/authorization_request/mod.rs index fa77ae9..68f4cf8 100644 --- a/src/core/authorization_request/mod.rs +++ b/src/core/authorization_request/mod.rs @@ -1,6 +1,7 @@ use std::ops::{Deref, DerefMut}; use anyhow::{anyhow, bail, Context, Error, Result}; +use parameters::ClientMetadata; use serde::{Deserialize, Serialize}; use serde_json::Value as Json; use url::Url; @@ -16,6 +17,7 @@ use self::{ }; use super::{ + metadata::parameters::verifier::VpFormats, object::{ParsingErrorContext, UntypedObject}, util::{base_request, AsyncHttpClient}, }; @@ -263,6 +265,21 @@ impl AuthorizationRequestObject { pub fn nonce(&self) -> &Nonce { &self.7 } + + /// Return the `client_metadata` field from the authorization request. + pub fn client_metadata(&self) -> Result { + self.0 + .get() + .ok_or(anyhow!("missing `client_metadata` object"))? + } + + /// Return the `VpFormats` from the `client_metadata` field. + pub fn vp_formats(&self) -> Result { + self.client_metadata()? + .0 + .get() + .ok_or(anyhow!("missing vp_formats"))? + } } impl From for UntypedObject { diff --git a/src/core/authorization_request/parameters.rs b/src/core/authorization_request/parameters.rs index 58429c2..a4578cc 100644 --- a/src/core/authorization_request/parameters.rs +++ b/src/core/authorization_request/parameters.rs @@ -1,13 +1,21 @@ use std::{fmt, ops::Deref}; use crate::core::{ + metadata::parameters::{ + verifier::{JWKs, VpFormats}, + wallet::{ + AuthorizationEncryptedResponseAlg, AuthorizationEncryptedResponseEnc, + AuthorizationSignedResponseAlg, + }, + }, object::{ParsingErrorContext, TypedParameter, UntypedObject}, presentation_definition::PresentationDefinition as PresentationDefinitionParsed, util::{base_request, AsyncHttpClient}, }; -use anyhow::{bail, Context, Error, Ok}; +use anyhow::{anyhow, bail, Context, Error, Ok}; use serde::{Deserialize, Serialize}; use serde_json::Value as Json; +use ssi::JWK; use url::Url; use super::AuthorizationRequestObject; @@ -105,6 +113,23 @@ impl fmt::Display for ClientIdScheme { } /// `client_metadata` field in the Authorization Request. +/// +/// client_metadata: OPTIONAL. A JSON object containing the Verifier metadata values. +/// It MUST be UTF-8 encoded. The following metadata parameters MAY be used: +/// +/// jwks: OPTIONAL. A JWKS as defined in [RFC7591]. It MAY contain one or more public keys, such as those used by the Wallet as an input to a key agreement that may be used for encryption of the Authorization Response (see Section 7.3), or where the Wallet will require the public key of the Verifier to generate the Verifiable Presentation. This allows the Verifier to pass ephemeral keys specific to this Authorization Request. Public keys included in this parameter MUST NOT be used to verify the signature of signed Authorization Requests. +/// vp_formats: REQUIRED when not available to the Wallet via another mechanism. As defined in Section 10.1. +/// authorization_signed_response_alg: OPTIONAL. As defined in [JARM]. +/// authorization_encrypted_response_alg: OPTIONAL. As defined in [JARM]. +/// authorization_encrypted_response_enc: OPTIONAL. As defined in [JARM]. +/// Authoritative data the Wallet is able to obtain about the Client from other sources, +/// for example those from an OpenID Federation Entity Statement, take precedence over the +/// values passed in client_metadata. Other metadata parameters MUST be ignored unless a +/// profile of this specification explicitly defines them as usable in the client_metadata parameter. +/// +/// +/// See reference: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-5.1-4.2.4 +/// #[derive(Debug, Clone)] pub struct ClientMetadata(pub UntypedObject); @@ -169,6 +194,117 @@ impl ClientMetadata { tracing::warn!("the client metadata was not passed by reference or value"); Ok(ClientMetadata(UntypedObject::default())) } + + /// OPTIONAL. A JWKS as defined in + /// [RFC7591](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#RFC7591). + /// + /// It MAY contain one or more public keys, such as those used by the Wallet as an input to a + /// key agreement that may be used for encryption of the Authorization Response + /// (see [Section 7.3](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#jarm)), + /// or where the Wallet will require the public key of the Verifier to generate the Verifiable Presentation. + /// + /// This allows the Verifier to pass ephemeral keys specific to this Authorization Request. + /// Public keys included in this parameter MUST NOT be used to verify the signature of signed Authorization Requests. + /// + /// + /// See reference: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-5.1-4.2.2.1 + /// + /// The jwks_uri or jwks metadata parameters can be used by clients to register their public encryption keys. + /// + /// See: https://openid.net/specs/oauth-v2-jarm-final.html#section-3-4 + /// + pub fn jwks(&self) -> Option> { + self.0.get() + } + + /// Return the `VpFormats` from the `client_metadata` field. + /// + /// vp_formats: REQUIRED when not available to the Wallet via another mechanism. + /// + /// As defined in [Section 10.1](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#client_metadata_parameters). + /// + /// See reference: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-5.1-4.2.2.2 + pub fn vp_formats(&self) -> Result { + self.0.get().ok_or(anyhow!("missing vp_formats"))? + } + + /// OPTIONAL. As defined in [JARM](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#JARM). + /// + /// JARM -> JWT Secured Authorization Response Mode for OAuth 2.0 + /// + /// The JWS [RFC7515](https://openid.net/specs/oauth-v2-jarm-final.html#RFC7515) + /// `alg` algorithm REQUIRED for signing authorization responses. + /// + /// If this is specified, the response will be signed using JWS and the configured algorithm. + /// + /// If unspecified, the default algorithm to use for signing authorization responses is RS256. + /// + /// The algorithm none is not allowed. + /// + /// A list of defined ["alg" values](https://datatracker.ietf.org/doc/html/rfc7518#section-3.1) + /// for this use can be found in the IANA "JSON Web Signature and Encryption Algorithms" registry established + /// by [JWA](https://www.rfc-editor.org/rfc/rfc7515.html#ref-JWA); the initial contents of this registry are the values + /// defined in Section 3.1 of [JWA](https://www.rfc-editor.org/rfc/rfc7515.html#ref-JWA). + /// + /// See: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-5.1-4.2.2.3 + /// See: https://openid.net/specs/oauth-v2-jarm-final.html#section-3-3.2.1 + /// See: https://datatracker.ietf.org/doc/html/rfc7518#section-3.1 + /// + pub fn authorization_signed_response_alg( + &self, + ) -> Result { + self.0.get().unwrap_or(Ok(AuthorizationSignedResponseAlg( + ssi::crypto::Algorithm::RS256, + ))) + } + + /// OPTIONAL. As defined in [JARM](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#JARM). + /// + /// JARM -> JWT Secured Authorization Response Mode for OAuth 2.0 + /// + /// The JWE [RFC7516](https://openid.net/specs/oauth-v2-jarm-final.html#RFC7516) + /// `alg` algorithm REQUIRED for encrypting authorization responses. + /// + /// If both signing and encryption are requested, the response will be signed then encrypted, + /// with the result being a Nested JWT, as defined in JWT + /// [RFC7519](https://openid.net/specs/oauth-v2-jarm-final.html#RFC7519). + /// + /// The default, if omitted, is that no encryption is performed. + /// + /// + /// See: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-5.1-4.2.2.4 + /// See: https://openid.net/specs/oauth-v2-jarm-final.html#section-3-3.4.1 + /// + pub fn authorization_encrypted_response_alg( + &self, + ) -> Option> { + self.0.get() + } + + /// OPTIONAL. As defined in [JARM](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#JARM). + /// + /// JARM -> JWT Secured Authorization Response Mode for OAuth 2.0 + /// + /// The JWE [RFC7516](https://openid.net/specs/oauth-v2-jarm-final.html#RFC7516) `enc` algorithm + /// REQUIRED for encrypting authorization responses. + /// + /// If `authorization_encrypted_response_alg` is specified, the default for this value is `A128CBC-HS256`. + /// + /// When `authorization_encrypted_response_enc` is included, authorization_encrypted_response_alg MUST also be provided. + /// + /// See: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-5.1-4.2.2.5 + /// See: https://openid.net/specs/oauth-v2-jarm-final.html#section-3-3.6.1 + /// + pub fn authorization_encrypted_response_enc( + &self, + ) -> Option> { + match self.0.get() { + Some(enc) => Some(enc), + None => self + .authorization_encrypted_response_alg() + .map(|_| Ok(AuthorizationEncryptedResponseEnc("A128CBC-HS256".into()))), + } + } } /// `client_metadata_uri` field in the Authorization Request. diff --git a/src/core/authorization_request/verification/did.rs b/src/core/authorization_request/verification/did.rs index cd69956..42fba26 100644 --- a/src/core/authorization_request/verification/did.rs +++ b/src/core/authorization_request/verification/did.rs @@ -40,6 +40,14 @@ pub async fn verify_with_resolver( bail!("request was signed with unsupported algorithm: {alg}") } + // This bypass is for unencoded JWT requests, but we will need to change this later + // so that trust is preserved when receiving unencoded requests + // NOTE: This requires that `Algorithm::None` is permitted in the wallet metadata + // Otherwise, this function will error in the previous assertion. + if alg.contains("none") { + return Ok(()); + } + let Json::String(kid) = headers .remove("kid") .context("'kid' was missing from jwt headers")? diff --git a/src/core/credential_format/mod.rs b/src/core/credential_format/mod.rs index f206ae5..c9d25f1 100644 --- a/src/core/credential_format/mod.rs +++ b/src/core/credential_format/mod.rs @@ -137,6 +137,7 @@ pub enum ClaimFormatPayload { /// claim presentation algorithm types supported by a wallet. #[serde(rename = "alg_values_supported")] AlgValuesSupported(Vec), + /// This variant is primarily used for `ldp`, `ldp_vc`, `ldp_vp`, `ac_vc`, and `ac_vp` #[serde(rename = "proof_type")] ProofType(Vec), #[serde(untagged)] diff --git a/src/core/input_descriptor.rs b/src/core/input_descriptor.rs index b9c26d8..0d7c91a 100644 --- a/src/core/input_descriptor.rs +++ b/src/core/input_descriptor.rs @@ -209,6 +209,11 @@ impl Constraints { self.fields.as_ref() } + /// Returns the fields of the constraints object as a mutable reference. + pub fn fields_mut(&mut self) -> &mut Vec { + self.fields.as_mut() + } + /// Set the limit disclosure value. /// /// For all [Claims](https://identity.foundation/presentation-exchange/spec/v2.0.0/#term:claims) submitted in relation to [InputDescriptor] Objects that include a `constraints` diff --git a/src/core/metadata/mod.rs b/src/core/metadata/mod.rs index 4ee44ef..b536f60 100644 --- a/src/core/metadata/mod.rs +++ b/src/core/metadata/mod.rs @@ -49,19 +49,38 @@ impl WalletMetadata { &mut self.2 } - /// Add a client ID scheme to the list of the client ID schemes supported. + /// Add a request object signing algorithm to the list of the request object + /// signing algorithms supported. + pub fn add_request_object_signing_alg_values_supported( + &mut self, + alg: Algorithm, + ) -> Result<()> { + let mut supported = self + .0 + .get_or_default::()?; + + // Insert the algorithm. + supported.0.push(alg.to_string()); + + // Insert the updated request object signing algorithms supported. + self.0.insert(supported); + + Ok(()) + } + + /// Appends the client ID schemes to the list of the client ID schemes supported. /// - /// This method will construct a `client_id_schemes_supported` proprety in the - /// wallet metadata if none exists previously, otherwise, this method will add - /// the client ID scheme to the existing list of the client ID schemes supported. + /// This method will construct a `client_id_schemes_supported` property in the + /// wallet metadata if none exists previously, otherwise, this method will append + /// the client ID schemes to the existing list of the client ID schemes supported. pub fn add_client_id_schemes_supported( &mut self, - client_id_scheme: ClientIdScheme, + client_id_schemes: &[ClientIdScheme], ) -> Result<()> { let mut supported = self.0.get_or_default::()?; // Insert the scheme. - supported.0.push(client_id_scheme); + supported.0.extend_from_slice(client_id_schemes); // Insert the updated client IDs schemes supported. self.0.insert(supported); diff --git a/src/core/metadata/parameters/verifier.rs b/src/core/metadata/parameters/verifier.rs index e66b9e0..99dc127 100644 --- a/src/core/metadata/parameters/verifier.rs +++ b/src/core/metadata/parameters/verifier.rs @@ -1,5 +1,6 @@ -use crate::core::credential_format::ClaimFormatMap; +use crate::core::metadata::ClaimFormatPayload; use crate::core::object::TypedParameter; +use crate::core::{credential_format::ClaimFormatMap, metadata::ClaimFormatDesignation}; use anyhow::{Context, Error}; use serde::{Deserialize, Serialize}; @@ -8,6 +9,34 @@ use serde_json::{Map, Value as Json}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct VpFormats(pub ClaimFormatMap); +impl VpFormats { + /// Returns a boolean to denote whether a particular pair of format and security method + /// are supported in the VP formats. A security method could be a JOSE algorithm, a COSE + /// algorithm, a Cryptosuite, etc. + /// + /// NOTE: This method is interested in the security method of the claim format + /// payload and not the claim format designation. + /// + /// For example, the security method would need to match one of the `alg` + /// values in the claim format payload. + pub fn supports_security_method( + &self, + format: &ClaimFormatDesignation, + security_method: &String, + ) -> bool { + match self.0.get(format) { + Some(ClaimFormatPayload::Alg(alg_values)) + | Some(ClaimFormatPayload::AlgValuesSupported(alg_values)) => { + alg_values.contains(security_method) + } + Some(ClaimFormatPayload::ProofType(proof_types)) => { + proof_types.contains(security_method) + } + _ => false, + } + } +} + impl TypedParameter for VpFormats { const KEY: &'static str = "vp_formats"; } diff --git a/src/core/metadata/parameters/wallet.rs b/src/core/metadata/parameters/wallet.rs index e36677c..fce2db1 100644 --- a/src/core/metadata/parameters/wallet.rs +++ b/src/core/metadata/parameters/wallet.rs @@ -113,7 +113,7 @@ impl Default for ClientIdSchemesSupported { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct RequestObjectSigningAlgValuesSupported(pub Vec); impl TypedParameter for RequestObjectSigningAlgValuesSupported { @@ -205,6 +205,73 @@ impl From for Json { } } +#[derive(Debug, Clone)] +pub struct AuthorizationSignedResponseAlg(pub ssi::crypto::Algorithm); + +impl TypedParameter for AuthorizationSignedResponseAlg { + const KEY: &'static str = "authorization_signed_response_alg"; +} + +impl TryFrom for AuthorizationSignedResponseAlg { + type Error = Error; + + fn try_from(value: Json) -> Result { + Ok(Self(serde_json::from_value(value)?)) + } +} + +impl From for Json { + fn from(value: AuthorizationSignedResponseAlg) -> Json { + Json::String(value.0.to_string()) + } +} + +#[derive(Debug, Clone)] +pub struct AuthorizationEncryptedResponseAlg(pub ssi::crypto::Algorithm); + +impl TypedParameter for AuthorizationEncryptedResponseAlg { + const KEY: &'static str = "authorization_encrypted_response_alg"; +} + +impl TryFrom for AuthorizationEncryptedResponseAlg { + type Error = Error; + + fn try_from(value: Json) -> Result { + Ok(Self(serde_json::from_value(value)?)) + } +} + +impl From for Json { + fn from(value: AuthorizationEncryptedResponseAlg) -> Json { + Json::String(value.0.to_string()) + } +} + +// TODO: ssi::crypto lacks an encryption algorithm enum type, +// which we may want to create for use cases like this one. +// +// Using a string type here in the interim. +#[derive(Debug, Clone)] +pub struct AuthorizationEncryptedResponseEnc(pub String); + +impl TypedParameter for AuthorizationEncryptedResponseEnc { + const KEY: &'static str = "authorization_encrypted_response_enc"; +} + +impl TryFrom for AuthorizationEncryptedResponseEnc { + type Error = Error; + + fn try_from(value: Json) -> Result { + Ok(Self(serde_json::from_value(value)?)) + } +} + +impl From for Json { + fn from(value: AuthorizationEncryptedResponseEnc) -> Json { + Json::String(value.0) + } +} + #[cfg(test)] mod test { use serde_json::json; diff --git a/src/core/object/mod.rs b/src/core/object/mod.rs index 02447c5..02ad16f 100644 --- a/src/core/object/mod.rs +++ b/src/core/object/mod.rs @@ -1,5 +1,3 @@ -use std::collections::BTreeMap; - use anyhow::{Context, Error, Result}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value as Json}; @@ -61,21 +59,6 @@ impl UntypedObject { ), } } - - /// Flatten the structure for posting as a form. - pub(crate) fn flatten_for_form(self) -> Result> { - 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 for Json { diff --git a/src/core/response/mod.rs b/src/core/response/mod.rs index a6e1b1b..b4a8c92 100644 --- a/src/core/response/mod.rs +++ b/src/core/response/mod.rs @@ -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; @@ -26,45 +20,72 @@ impl AuthorizationResponse { return Ok(Self::Jwt(jwt)); } - let flattened = serde_urlencoded::from_bytes::>(bytes) + let unencoded = serde_urlencoded::from_bytes::(bytes) .context("failed to construct flat map")?; - let map = flattened - .into_iter() - .map(|(k, v)| { - let v = serde_json::from_str::(&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 { - 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 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, + } } } @@ -91,9 +112,7 @@ impl TryFrom for UnencodedAuthorizationResponse { type Error = Error; fn try_from(value: UntypedObject) -> Result { - 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))?) } } @@ -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")); } } diff --git a/src/core/response/parameters.rs b/src/core/response/parameters.rs index c997283..f2522d1 100644 --- a/src/core/response/parameters.rs +++ b/src/core/response/parameters.rs @@ -4,7 +4,13 @@ use crate::core::object::TypedParameter; use anyhow::Error; use serde::{Deserialize, Serialize}; use serde_json::Value as Json; -use ssi::{claims::vc, one_or_many::OneOrManyRef, prelude::AnyJsonPresentation, OneOrMany}; +use ssi::{ + claims::vc::{self, v2::SpecializedJsonCredential}, + json_ld::syntax::Object, + one_or_many::OneOrManyRef, + prelude::{AnyDataIntegrity, AnyJsonPresentation, AnySuite, DataIntegrity}, + OneOrMany, +}; #[derive(Debug, Clone)] pub struct IdToken(pub String); @@ -106,6 +112,12 @@ impl From for VpToken { } } +impl From>> for VpToken { + fn from(value: vc::v2::syntax::JsonPresentation>) -> Self { + Self(vec![value.into()]) + } +} + impl From for VpToken { fn from(value: AnyJsonPresentation) -> Self { Self(vec![value.into()]) @@ -159,6 +171,20 @@ impl From for VpTokenItem { } } +impl From for VpTokenItem { + fn from(value: AnyDataIntegrity) -> Self { + let serde_json::Value::Object(obj) = serde_json::to_value(&value) + // SAFETY: by definition a Data Integrity Object is a Json LD Node and is a JSON object. + .unwrap() + else { + // SAFETY: by definition a Data Integrity Object is a Json LD Node and is a JSON object. + unreachable!() + }; + + Self::JsonObject(obj) + } +} + impl From for VpTokenItem { fn from(value: vc::v1::syntax::JsonPresentation) -> Self { let serde_json::Value::Object(obj) = serde_json::to_value(value) @@ -187,6 +213,20 @@ impl From for VpTokenItem { } } +impl From>> for VpTokenItem { + fn from(value: vc::v2::syntax::JsonPresentation>) -> Self { + let serde_json::Value::Object(obj) = serde_json::to_value(value) + // SAFETY: by definition a VCDM2.0 presentation is a JSON object. + .unwrap() + else { + // SAFETY: by definition a VCDM2.0 presentation is a JSON object. + unreachable!() + }; + + Self::JsonObject(obj) + } +} + impl From for VpTokenItem { fn from(value: AnyJsonPresentation) -> Self { let serde_json::Value::Object(obj) = serde_json::to_value(value) @@ -200,3 +240,17 @@ impl From for VpTokenItem { Self::JsonObject(obj) } } + +impl From> for VpTokenItem { + fn from(value: DataIntegrity) -> Self { + let serde_json::Value::Object(obj) = serde_json::to_value(value) + // SAFETY: by definition a VCDM2.0 presentation is a JSON object. + .unwrap() + else { + // SAFETY: by definition a VCDM2.0 presentation is a JSON object. + unreachable!() + }; + + Self::JsonObject(obj) + } +} diff --git a/tests/e2e.rs b/tests/e2e.rs index 1792988..1c3c9a0 100644 --- a/tests/e2e.rs +++ b/tests/e2e.rs @@ -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);