use crate::auth::Claims;
use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct JwtConfig {
pub secret: String,
pub issuer: String,
pub audience: String,
pub expiry: Duration,
}
impl JwtConfig {
pub fn new(secret: &str) -> Self {
Self {
secret: secret.to_string(),
issuer: "kegani".to_string(),
audience: "kegani-api".to_string(),
expiry: Duration::from_secs(3600 * 24), }
}
pub fn issuer(mut self, issuer: &str) -> Self {
self.issuer = issuer.to_string();
self
}
pub fn audience(mut self, audience: &str) -> Self {
self.audience = audience.to_string();
self
}
pub fn expiry(mut self, duration: Duration) -> Self {
self.expiry = duration;
self
}
}
pub struct JwtAuth {
config: JwtConfig,
}
impl JwtAuth {
pub fn new(config: JwtConfig) -> Self {
Self { config }
}
pub fn generate(&self, claims: &Claims) -> Result<String, JwtError> {
use jsonwebtoken::{encode, Header, Algorithm, EncodingKey};
let header = Header::new(Algorithm::HS256);
let now = Instant::now().elapsed().as_secs();
#[derive(Serialize)]
struct JwtClaims {
sub: String,
iat: i64,
exp: i64,
roles: Vec<String>,
}
let jwt_claims = JwtClaims {
sub: claims.subject.clone(),
iat: now as i64,
exp: (now + self.config.expiry.as_secs()) as i64,
roles: claims.roles.clone(),
};
let key = EncodingKey::from_secret(self.config.secret.as_bytes());
encode(&header, &jwt_claims, &key)
.map_err(|e| JwtError::Generation(e.to_string()))
}
pub fn verify(&self, token: &str) -> Result<Claims, JwtError> {
use jsonwebtoken::{decode, DecodingKey, Validation, Algorithm};
let mut validation = Validation::new(Algorithm::HS256);
validation.set_issuer(&[&self.config.issuer]);
validation.set_audience(&[&self.config.audience]);
let key = DecodingKey::from_secret(self.config.secret.as_bytes());
#[derive(Deserialize)]
struct JwtClaims {
sub: String,
roles: Vec<String>,
}
let token_data = decode::<JwtClaims>(token, &key, &validation)
.map_err(|e| JwtError::Verification(e.to_string()))?;
Ok(Claims {
subject: token_data.claims.sub,
roles: token_data.claims.roles,
extra: None,
})
}
}
#[derive(Debug)]
pub enum JwtError {
Generation(String),
Verification(String),
}
impl std::fmt::Display for JwtError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
JwtError::Generation(e) => write!(f, "JWT generation error: {}", e),
JwtError::Verification(e) => write!(f, "JWT verification error: {}", e),
}
}
}
impl std::error::Error for JwtError {}