From ad157be7338afeac66549345355b83a5675976b5 Mon Sep 17 00:00:00 2001 From: Duncan Lutz Date: Tue, 17 Feb 2026 10:49:31 -0700 Subject: [PATCH 1/3] fix: updating jwt serialization --- rust/src/oid4vci/credential.rs | 5 ++- rust/src/tests.rs | 67 ++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/rust/src/oid4vci/credential.rs b/rust/src/oid4vci/credential.rs index 8a289534..ff9be082 100644 --- a/rust/src/oid4vci/credential.rs +++ b/rust/src/oid4vci/credential.rs @@ -27,7 +27,10 @@ impl RawCredential { W3cVcFormat::JwtVcJson => CredentialFormat::JwtVcJson, W3cVcFormat::JwtVcJsonLd => CredentialFormat::JwtVcJsonLd, }, - payload: serde_json::to_vec(&credential.value).unwrap(), + payload: match credential.value { + serde_json::Value::String(s) => s.into_bytes(), + value => serde_json::to_vec(&value).unwrap(), + }, }), StandardFormat::MsoMdoc => match credential.value { serde_json::Value::String(base64_mso_mdoc) => Ok(Self { diff --git a/rust/src/tests.rs b/rust/src/tests.rs index be362f3f..130ae463 100644 --- a/rust/src/tests.rs +++ b/rust/src/tests.rs @@ -100,3 +100,70 @@ pub async fn test_vc_playground_oid4vp() { .await .expect("Permission response submission failed"); } + +#[cfg(test)] +mod tests { + use oid4vci::{ + profile::{StandardFormat, W3cVcFormat}, + Oid4vciCredential, + }; + + use crate::credential::{CredentialFormat, RawCredential}; + + #[test] + fn jwt_vc_json_payload_not_double_encoded() { + let jwt = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.\ + eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIn0.\ + signature"; + + let credential = Oid4vciCredential::new(serde_json::Value::String(jwt.to_owned())); + let format = StandardFormat::W3c(W3cVcFormat::JwtVcJson); + + let raw = RawCredential::from_oid4vci(&format, credential).unwrap(); + + assert_eq!(raw.format, CredentialFormat::JwtVcJson); + assert_eq!( + raw.payload, + jwt.as_bytes(), + "payload should be the raw JWT bytes, not JSON-encoded with quotes" + ); + } + + #[test] + fn jwt_vc_json_ld_payload_not_double_encoded() { + let jwt = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.\ + eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIn0.\ + signature"; + + let credential = Oid4vciCredential::new(serde_json::Value::String(jwt.to_owned())); + let format = StandardFormat::W3c(W3cVcFormat::JwtVcJsonLd); + + let raw = RawCredential::from_oid4vci(&format, credential).unwrap(); + + assert_eq!(raw.format, CredentialFormat::JwtVcJsonLd); + assert_eq!( + raw.payload, + jwt.as_bytes(), + "payload should be the raw JWT bytes, not JSON-encoded with quotes" + ); + } + + #[test] + fn ldp_vc_payload_is_json_object() { + let vc = serde_json::json!({ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "type": ["VerifiableCredential"], + "issuer": "https://example.com", + "credentialSubject": {} + }); + + let credential = Oid4vciCredential::new(vc.clone()); + let format = StandardFormat::W3c(W3cVcFormat::LdpVc); + + let raw = RawCredential::from_oid4vci(&format, credential).unwrap(); + + assert_eq!(raw.format, CredentialFormat::LdpVc); + let roundtripped: serde_json::Value = serde_json::from_slice(&raw.payload).unwrap(); + assert_eq!(roundtripped, vc); + } +} From afde8f672dd720dde643c11479b73d48581460c0 Mon Sep 17 00:00:00 2001 From: Duncan Lutz Date: Tue, 17 Feb 2026 11:01:51 -0700 Subject: [PATCH 2/3] Update rust/src/oid4vci/credential.rs Add comment verifying why unwrap is acceptable in this case Co-authored-by: Ryan Tate --- rust/src/oid4vci/credential.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/src/oid4vci/credential.rs b/rust/src/oid4vci/credential.rs index ff9be082..98a5bad1 100644 --- a/rust/src/oid4vci/credential.rs +++ b/rust/src/oid4vci/credential.rs @@ -29,6 +29,7 @@ impl RawCredential { }, payload: match credential.value { serde_json::Value::String(s) => s.into_bytes(), + # SAFETY: value is `serde_json::Value` value => serde_json::to_vec(&value).unwrap(), }, }), From 6995335b425791a3f6c6eb7f87a48cee69e014a0 Mon Sep 17 00:00:00 2001 From: Ryan Tate Date: Tue, 17 Feb 2026 10:53:06 -0800 Subject: [PATCH 3/3] Apply suggestion from @Ryanmtate --- rust/src/oid4vci/credential.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/src/oid4vci/credential.rs b/rust/src/oid4vci/credential.rs index 98a5bad1..16ea1504 100644 --- a/rust/src/oid4vci/credential.rs +++ b/rust/src/oid4vci/credential.rs @@ -29,7 +29,7 @@ impl RawCredential { }, payload: match credential.value { serde_json::Value::String(s) => s.into_bytes(), - # SAFETY: value is `serde_json::Value` + // SAFETY: value is `serde_json::Value` value => serde_json::to_vec(&value).unwrap(), }, }),