use chrono::prelude::*;
use jsonwebtoken::{DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Claims {
pub sub: String,
pub company: String,
#[serde(with = "jwt_numeric_date")]
pub exp: DateTime<Utc>,
}
impl Claims {
pub fn new(sub: String, company: String, exp: DateTime<Utc>) -> Self {
let exp = exp
.date_naive()
.and_hms_milli_opt(exp.hour(), exp.minute(), exp.second(), 0)
.unwrap();
let exp = Utc.from_local_datetime(&exp).unwrap();
Self { sub, company, exp }
}
}
mod jwt_numeric_date {
use chrono::{DateTime, TimeZone, Utc};
use serde::{self, Deserialize, Deserializer, Serializer};
pub fn serialize<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let timestamp = date.timestamp();
serializer.serialize_i64(timestamp)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
Utc.timestamp_opt(i64::deserialize(deserializer)?, 0)
.single() .ok_or_else(|| serde::de::Error::custom("invalid Unix timestamp value"))
}
}
pub fn validate_token(
token: &str,
secret: Option<String>,
) -> Result<bool, Box<dyn std::error::Error>> {
let _token_data = jsonwebtoken::decode::<Claims>(
token,
&DecodingKey::from_secret(secret.unwrap_or("default".to_string()).as_bytes()),
&Validation::default(),
)?;
Ok(true)
}
pub fn decode_token(
token: &str,
secret: &Option<String>,
) -> Result<String, Box<dyn std::error::Error>> {
let token_data = jsonwebtoken::decode::<Claims>(
token,
&DecodingKey::from_secret(
secret
.to_owned()
.unwrap_or(String::from("default"))
.as_bytes(),
),
&Validation::default(),
)?;
Ok(token_data.claims.company)
}
pub fn create_token(
user: &str,
duration: i64,
secret: Option<String>,
) -> Result<String, Box<dyn std::error::Error>> {
let sub = "user".to_string();
let company = user.to_string();
let exp = Utc::now() + chrono::Duration::days(duration);
let claims = Claims::new(sub, company, exp);
let token = jsonwebtoken::encode(
&Header::default(),
&claims,
&EncodingKey::from_secret(secret.unwrap_or(String::from("default")).as_bytes()),
)?;
Ok(token)
}