use std::borrow::Cow;
use serde::Deserialize;
use serde::Serialize;
use identity_core::common::Context;
use identity_core::common::Object;
use identity_core::common::OneOrMany;
use identity_core::common::Url;
use serde::de::DeserializeOwned;
use crate::credential::IssuanceDateClaims;
use crate::credential::Jwt;
use crate::credential::Policy;
use crate::credential::Proof;
use crate::credential::RefreshService;
use crate::presentation::Presentation;
#[cfg(feature = "validator")]
use crate::Error;
use crate::Result;
use super::JwtPresentationOptions;
#[derive(Serialize, Deserialize)]
pub(crate) struct PresentationJwtClaims<'presentation, CRED, T = Object>
where
T: ToOwned + Serialize,
CRED: ToOwned + Serialize + Clone,
<CRED as ToOwned>::Owned: DeserializeOwned,
<T as ToOwned>::Owned: DeserializeOwned,
{
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) exp: Option<i64>,
pub(crate) iss: Cow<'presentation, Url>,
#[serde(flatten)]
pub(crate) issuance_date: Option<IssuanceDateClaims>,
#[serde(skip_serializing_if = "Option::is_none")]
jti: Option<Cow<'presentation, Url>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) aud: Option<Url>,
pub(crate) vp: InnerPresentation<'presentation, CRED, T>,
}
impl<'presentation, CRED, T> PresentationJwtClaims<'presentation, CRED, T>
where
T: ToOwned<Owned = T> + Serialize + DeserializeOwned,
CRED: ToOwned<Owned = CRED> + Serialize + DeserializeOwned + Clone,
{
pub(super) fn new(
presentation: &'presentation Presentation<CRED, T>,
options: &JwtPresentationOptions,
) -> Result<Self> {
let Presentation {
context,
id,
types,
verifiable_credential,
holder,
refresh_service,
terms_of_use,
properties,
proof,
} = presentation;
Ok(Self {
iss: Cow::Borrowed(holder),
jti: id.as_ref().map(Cow::Borrowed),
vp: InnerPresentation {
context: Cow::Borrowed(context),
id: None,
types: Cow::Borrowed(types),
verifiable_credential: Cow::Borrowed(verifiable_credential),
refresh_service: Cow::Borrowed(refresh_service),
terms_of_use: Cow::Borrowed(terms_of_use),
properties: Cow::Borrowed(properties),
proof: proof.as_ref().map(Cow::Borrowed),
},
exp: options.expiration_date.map(|expiration_date| expiration_date.to_unix()),
issuance_date: options.issuance_date.map(IssuanceDateClaims::new),
aud: options.audience.clone(),
})
}
}
#[derive(Serialize, Deserialize)]
pub(crate) struct InnerPresentation<'presentation, CRED = Jwt, T = Object>
where
CRED: Clone + Serialize,
T: ToOwned + Serialize,
<T as ToOwned>::Owned: DeserializeOwned,
{
#[serde(rename = "@context")]
context: Cow<'presentation, OneOrMany<Context>>,
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<Url>,
#[serde(rename = "type")]
types: Cow<'presentation, OneOrMany<String>>,
#[serde(default = "Default::default", rename = "verifiableCredential")]
pub(crate) verifiable_credential: Cow<'presentation, OneOrMany<CRED>>,
#[serde(default, rename = "refreshService", skip_serializing_if = "OneOrMany::is_empty")]
refresh_service: Cow<'presentation, OneOrMany<RefreshService>>,
#[serde(default, rename = "termsOfUse", skip_serializing_if = "OneOrMany::is_empty")]
terms_of_use: Cow<'presentation, OneOrMany<Policy>>,
#[serde(flatten)]
properties: Cow<'presentation, T>,
#[serde(skip_serializing_if = "Option::is_none")]
proof: Option<Cow<'presentation, Proof>>,
}
#[cfg(feature = "validator")]
impl<'presentation, CRED, T> PresentationJwtClaims<'presentation, CRED, T>
where
CRED: ToOwned<Owned = CRED> + Serialize + DeserializeOwned + Clone,
T: ToOwned<Owned = T> + Serialize + DeserializeOwned,
{
pub(crate) fn try_into_presentation(self) -> Result<Presentation<CRED, T>> {
self.check_consistency()?;
let Self {
exp: _,
iss,
issuance_date: _,
jti,
aud: _,
vp,
} = self;
let InnerPresentation {
context,
id: _,
types,
verifiable_credential,
refresh_service,
terms_of_use,
properties,
proof,
} = vp;
let presentation = Presentation {
context: context.into_owned(),
id: jti.map(Cow::into_owned),
types: types.into_owned(),
verifiable_credential: verifiable_credential.into_owned(),
holder: iss.into_owned(),
refresh_service: refresh_service.into_owned(),
terms_of_use: terms_of_use.into_owned(),
properties: properties.into_owned(),
proof: proof.map(Cow::into_owned),
};
Ok(presentation)
}
fn check_consistency(&self) -> Result<()> {
if !self
.vp
.id
.as_ref()
.map(|value| self.jti.as_ref().filter(|jti| jti.as_ref() == value).is_some())
.unwrap_or(true)
{
return Err(Error::InconsistentPresentationJwtClaims("inconsistent presentation id"));
};
Ok(())
}
}