Skip to content

Commit

Permalink
update vp token definition
Browse files Browse the repository at this point in the history
Signed-off-by: Ryan Tate <ryan.michael.tate@gmail.com>
  • Loading branch information
Ryanmtate committed Sep 18, 2024
1 parent 1e8cad2 commit 81cd134
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 32 deletions.
79 changes: 64 additions & 15 deletions src/core/response/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use crate::core::object::TypedParameter;
use crate::core::presentation_submission::PresentationSubmission as PresentationSubmissionParsed;

use anyhow::Error;
use base64::prelude::*;
use serde_json::Value as Json;
use ssi::claims::jwt::VerifiablePresentation;

#[derive(Debug, Clone)]
pub struct IdToken(pub String);
Expand All @@ -26,18 +28,26 @@ impl From<IdToken> for Json {
}
}

// TODO: Update this type to something like:
//
// enum VpToken {
// Single(String),
// SingleAsMap(Map<String, Value>),
// Many(Vec<VpToken>),
// }
//
// See: https://github.com/spruceid/oid4vp-rs/pull/8#discussion_r1750274969
//
/// OpenID Connect for Verifiable Presentations specification defines `vp_token` parameter:
///
/// > JSON String or JSON object that MUST contain a single Verifiable Presentation or
/// > an array of JSON Strings and JSON objects each of them containing a Verifiable Presentations.
/// >
/// > Each Verifiable Presentation MUST be represented as a JSON string (that is a Base64url encoded value)
/// > or a JSON object depending on a format as defined in Appendix A of [OpenID.VCI].
/// >
/// > If Appendix A of [OpenID.VCI] defines a rule for encoding the respective Credential
/// > format in the Credential Response, this rules MUST also be followed when encoding Credentials of
/// > this format in the vp_token response parameter. Otherwise, this specification does not require
/// > any additional encoding when a Credential format is already represented as a JSON object or a JSON string.
///
/// See: [OpenID.VP#section-6.1-2.2](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-6.1-2.2)
#[derive(Debug, Clone)]
pub struct VpToken(pub String);
pub enum VpToken {
Single(String),
SingleAsMap(Json),
Many(Vec<VpToken>),
}

impl TypedParameter for VpToken {
const KEY: &'static str = "vp_token";
Expand All @@ -47,13 +57,52 @@ impl TryFrom<Json> for VpToken {
type Error = Error;

fn try_from(value: Json) -> Result<Self, Self::Error> {
serde_json::from_value(value).map(Self).map_err(Into::into)
match value {
// NOTE: When parsing a Json string object, it must be base64 encoded.
Json::String(s) => Ok(Self::Single(BASE64_STANDARD.encode(s.as_bytes()))),
// NOTE: When the Json is an object, it must be a map.
Json::Object(map) => Ok(Self::SingleAsMap(Json::Object(map))),
Json::Array(arr) => {
let mut tokens = Vec::new();
for value in arr {
tokens.push(Self::try_from(value)?);
}
Ok(Self::Many(tokens))
}
_ => Err(Error::msg("Invalid vp_token")),
}
}
}

impl From<VpToken> for Json {
fn from(value: VpToken) -> Self {
value.0.into()
impl TryFrom<VpToken> for Json {
type Error = Error;

fn try_from(value: VpToken) -> Result<Self, Self::Error> {
match value {
VpToken::Single(s) => {
// Decode base64 string.
let bytes = BASE64_STANDARD.decode(s.as_bytes())?;
let s = String::from_utf8(bytes)?;

Ok(s.into())
}
VpToken::SingleAsMap(map) => Ok(map),
VpToken::Many(tokens) => {
let mut arr: Vec<Json> = Vec::new();
for token in tokens {
arr.push(token.try_into()?);
}
Ok(arr.into())
}
}
}
}

impl TryFrom<VerifiablePresentation> for VpToken {
type Error = Error;

fn try_from(vp: VerifiablePresentation) -> Result<Self, Self::Error> {
Self::try_from(vp.0.into_serde_json())
}
}

Expand Down
13 changes: 6 additions & 7 deletions tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use oid4vp::{
object::UntypedObject,
presentation_definition::*,
presentation_submission::*,
response::{parameters::VpToken, AuthorizationResponse, UnencodedAuthorizationResponse},
response::{AuthorizationResponse, UnencodedAuthorizationResponse},
},
verifier::session::{Outcome, Status},
wallet::Wallet,
Expand Down Expand Up @@ -129,14 +129,13 @@ async fn w3c_vc_did_client_direct_post() {
descriptor_map,
);

let vp = create_test_verifiable_presentation()
.await
.expect("failed to create verifiable presentation");

let response = AuthorizationResponse::Unencoded(UnencodedAuthorizationResponse(
Default::default(),
VpToken(
create_test_verifiable_presentation()
.await
.expect("failed to create verifiable presentation")
.to_string(),
),
vp.try_into().expect("failed to convert vp to vp token"),
presentation_submission.try_into().unwrap(),
));

Expand Down
13 changes: 3 additions & 10 deletions tests/jwt_vp.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use std::str::FromStr;

use anyhow::Result;
use base64::prelude::*;
use oid4vp::holder::verifiable_presentation_builder::{
VerifiablePresentationBuilder, VerifiablePresentationBuilderOptions,
};
use oid4vp::verifier::request_signer::P256Signer;
use ssi::claims::jwt;
use ssi::claims::jwt::{self, VerifiablePresentation};
use ssi::dids::DIDKey;
use ssi::jwk::JWK;

pub async fn create_test_verifiable_presentation() -> Result<String> {
pub async fn create_test_verifiable_presentation() -> Result<VerifiablePresentation> {
let verifier = JWK::from_str(include_str!("examples/verifier.jwk"))?;

let signer = P256Signer::new(
Expand Down Expand Up @@ -38,11 +37,5 @@ pub async fn create_test_verifiable_presentation() -> Result<String> {
nonce: "random_nonce".into(),
});

// Encode the verifiable presentation as base64 encoded payload.
let vp_token = verifiable_presentation.0.to_string();

// encode as base64.
let base64_encoded_vp = BASE64_STANDARD.encode(vp_token);

Ok(base64_encoded_vp)
Ok(verifiable_presentation)
}

0 comments on commit 81cd134

Please sign in to comment.