From 809f242427cf8aba3c3511fd33405be9c83cc011 Mon Sep 17 00:00:00 2001 From: "Charles E. Lehner" Date: Tue, 28 Sep 2021 15:58:58 -0400 Subject: [PATCH] Allow JWT VC with single-element-array as subject --- src/one_or_many.rs | 13 +++++++++++++ src/vc.rs | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/one_or_many.rs b/src/one_or_many.rs index ad6bff718..54c3138bf 100644 --- a/src/one_or_many.rs +++ b/src/one_or_many.rs @@ -67,6 +67,19 @@ impl OneOrMany { } } } + + pub fn to_single_mut(&mut self) -> Option<&mut T> { + match self { + Self::One(value) => Some(value), + Self::Many(values) => { + if values.len() == 1 { + Some(&mut values[0]) + } else { + None + } + } + } + } } // consuming iterator diff --git a/src/vc.rs b/src/vc.rs index 8b183dc3b..c67cef1f4 100644 --- a/src/vc.rs +++ b/src/vc.rs @@ -761,7 +761,7 @@ impl Credential { } if let Some(sub) = claims.subject { if let StringOrURI::URI(sub_uri) = sub { - if let OneOrMany::One(ref mut subject) = vc.credential_subject { + if let Some(ref mut subject) = vc.credential_subject.to_single_mut() { subject.id = Some(sub_uri); } else { return Err(Error::InvalidSubject); @@ -2334,6 +2334,48 @@ pub(crate) mod tests { assert!(verification_result.errors.len() > 0); } + #[async_std::test] + async fn decode_verify_jwt_single_array_subject() { + let key: JWK = serde_json::from_str(JWK_JSON).unwrap(); + + let vc_str = r###"{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "type": "VerifiableCredential", + "issuer": "did:example:foo", + "issuanceDate": "2021-09-28T19:58:30Z", + "credentialSubject": [{ + "id": "did:example:a6c78986cc36418b95a22d7f736", + "spouse": "Example Person" + }] + }"###; + + let vc = Credential { + expiration_date: Some(VCDateTime::from(Utc::now() + chrono::Duration::weeks(1))), + ..serde_json::from_str(vc_str).unwrap() + }; + let aud = "did:example:90336644520443d28ba78beb949".to_string(); + let options = LinkedDataProofOptions { + domain: Some(aud), + checks: None, + created: None, + verification_method: Some(URI::String("did:example:foo#key1".to_string())), + ..Default::default() + }; + let signed_jwt = vc + .generate_jwt(Some(&key), &options, &DIDExample) + .await + .unwrap(); + println!("{:?}", signed_jwt); + + let (vc1_opt, verification_result) = + Credential::decode_verify_jwt(&signed_jwt, Some(options.clone()), &DIDExample).await; + println!("{:#?}", verification_result); + assert!(verification_result.errors.is_empty()); + } + #[async_std::test] async fn credential_issue_verify() { let vc_str = r###"{