identity_credential/sd_jwt_vc/
claims.rsuse std::ops::Deref;
use std::ops::DerefMut;
use identity_core::common::StringOrUrl;
use identity_core::common::Timestamp;
use identity_core::common::Url;
use sd_jwt_payload_rework::Disclosure;
use sd_jwt_payload_rework::SdJwtClaims;
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
use super::Error;
use super::Result;
use super::Status;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SdJwtVcClaims {
pub iss: Url,
pub nbf: Option<Timestamp>,
pub exp: Option<Timestamp>,
pub vct: StringOrUrl,
pub status: Option<Status>,
pub iat: Option<Timestamp>,
pub sub: Option<StringOrUrl>,
#[serde(flatten)]
pub(crate) sd_jwt_claims: SdJwtClaims,
}
impl Deref for SdJwtVcClaims {
type Target = SdJwtClaims;
fn deref(&self) -> &Self::Target {
&self.sd_jwt_claims
}
}
impl DerefMut for SdJwtVcClaims {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.sd_jwt_claims
}
}
impl SdJwtVcClaims {
pub(crate) fn try_from_sd_jwt_claims(mut claims: SdJwtClaims, disclosures: &[Disclosure]) -> Result<Self> {
let check_disclosed = |claim_name: &'static str| {
disclosures
.iter()
.any(|disclosure| disclosure.claim_name.as_deref() == Some(claim_name))
.then_some(Error::DisclosedClaim(claim_name))
};
let iss = claims
.remove("iss")
.ok_or(Error::MissingClaim("iss"))
.map_err(|e| check_disclosed("iss").unwrap_or(e))
.and_then(|value| {
value
.as_str()
.and_then(|s| Url::parse(s).ok())
.ok_or_else(|| Error::InvalidClaimValue {
name: "iss",
expected: "URL",
found: value,
})
})?;
let nbf = {
if let Some(value) = claims.remove("nbf") {
value
.as_number()
.and_then(|t| t.as_i64())
.and_then(|t| Timestamp::from_unix(t).ok())
.ok_or_else(|| Error::InvalidClaimValue {
name: "nbf",
expected: "unix timestamp",
found: value,
})
.map(Some)?
} else {
if let Some(err) = check_disclosed("nbf") {
return Err(err);
}
None
}
};
let exp = {
if let Some(value) = claims.remove("exp") {
value
.as_number()
.and_then(|t| t.as_i64())
.and_then(|t| Timestamp::from_unix(t).ok())
.ok_or_else(|| Error::InvalidClaimValue {
name: "exp",
expected: "unix timestamp",
found: value,
})
.map(Some)?
} else {
if let Some(err) = check_disclosed("exp") {
return Err(err);
}
None
}
};
let vct = claims
.remove("vct")
.ok_or(Error::MissingClaim("vct"))
.map_err(|e| check_disclosed("vct").unwrap_or(e))
.and_then(|value| {
value
.as_str()
.and_then(|s| StringOrUrl::parse(s).ok())
.ok_or_else(|| Error::InvalidClaimValue {
name: "vct",
expected: "String or URL",
found: value,
})
})?;
let status = {
if let Some(value) = claims.remove("status") {
serde_json::from_value::<Status>(value.clone())
.map_err(|_| Error::InvalidClaimValue {
name: "status",
expected: "credential's status object",
found: value,
})
.map(Some)?
} else {
if let Some(err) = check_disclosed("status") {
return Err(err);
}
None
}
};
let sub = claims
.remove("sub")
.map(|value| {
value
.as_str()
.and_then(|s| StringOrUrl::parse(s).ok())
.ok_or_else(|| Error::InvalidClaimValue {
name: "sub",
expected: "String or URL",
found: value,
})
})
.transpose()?;
let iat = claims
.remove("iat")
.map(|value| {
value
.as_number()
.and_then(|t| t.as_i64())
.and_then(|t| Timestamp::from_unix(t).ok())
.ok_or_else(|| Error::InvalidClaimValue {
name: "iat",
expected: "unix timestamp",
found: value,
})
})
.transpose()?;
Ok(Self {
iss,
nbf,
exp,
vct,
status,
iat,
sub,
sd_jwt_claims: claims,
})
}
}
impl From<SdJwtVcClaims> for SdJwtClaims {
fn from(claims: SdJwtVcClaims) -> Self {
let SdJwtVcClaims {
iss,
nbf,
exp,
vct,
status,
iat,
sub,
mut sd_jwt_claims,
} = claims;
sd_jwt_claims.insert("iss".to_string(), Value::String(iss.into_string()));
nbf.and_then(|t| sd_jwt_claims.insert("nbf".to_string(), Value::Number(t.to_unix().into())));
exp.and_then(|t| sd_jwt_claims.insert("exp".to_string(), Value::Number(t.to_unix().into())));
sd_jwt_claims.insert("vct".to_string(), Value::String(vct.into()));
status.and_then(|status| sd_jwt_claims.insert("status".to_string(), serde_json::to_value(status).unwrap()));
iat.and_then(|t| sd_jwt_claims.insert("iat".to_string(), Value::Number(t.to_unix().into())));
sub.and_then(|sub| sd_jwt_claims.insert("sub".to_string(), Value::String(sub.into())));
sd_jwt_claims
}
}