use std::collections::HashMap;
use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH};
use super::ClaimsBuildError;
#[derive(Serialize, Deserialize)]
pub struct Claims {
user_id: String,
iss: String,
exp: u64,
#[serde(flatten)]
#[serde(skip_serializing_if = "HashMap::is_empty")]
custom_claims: HashMap<String, String>,
}
impl Claims {
pub fn user_id(&self) -> String {
self.user_id.to_owned()
}
pub fn iss(&self) -> String {
self.iss.to_owned()
}
pub fn exp(&self) -> u64 {
self.exp
}
pub fn custom_claims(&self) -> HashMap<String, String> {
self.custom_claims.clone()
}
}
#[derive(Default)]
pub struct ClaimsBuilder {
user_id: Option<String>,
iss: Option<String>,
duration: Option<Duration>,
custom_claims: HashMap<String, String>,
}
impl ClaimsBuilder {
pub fn with_user_id(mut self, user_id: &str) -> Self {
self.user_id = Some(user_id.to_string());
self
}
pub fn with_issuer(mut self, iss: &str) -> Self {
self.iss = Some(iss.to_string());
self
}
pub fn with_duration(mut self, duration: Duration) -> Self {
self.duration = Some(duration);
self
}
pub fn with_custom_claim(mut self, key: &str, value: &str) -> Self {
self.custom_claims
.insert(key.to_string(), value.to_string());
self
}
pub fn build(self) -> Result<Claims, ClaimsBuildError> {
let user_id = self
.user_id
.ok_or_else(|| ClaimsBuildError::MissingRequiredField("Missing user_id".to_string()))?;
let iss = self
.iss
.ok_or_else(|| ClaimsBuildError::MissingRequiredField("Missing iss".to_string()))?;
let duration = self.duration.ok_or_else(|| {
ClaimsBuildError::MissingRequiredField("Missing claim duration".to_string())
})?;
let token_expiration_date = SystemTime::now().checked_add(duration).ok_or_else(|| {
ClaimsBuildError::InvalidValue(format!("Invalid duration for claim: {:?}", duration))
})?;
let token_expiration_timestamp = get_timestamp(token_expiration_date).map_err(|err| {
ClaimsBuildError::InvalidValue(format!("Invalid duration for claim: {}", err))
})?;
Ok(Claims {
user_id,
iss,
exp: token_expiration_timestamp,
custom_claims: self.custom_claims,
})
}
}
fn get_timestamp(time: SystemTime) -> Result<u64, SystemTimeError> {
Ok(time.duration_since(UNIX_EPOCH)?.as_secs())
}