use crate::config::Config;
use jsonwebtoken::{decode, DecodingKey, Validation};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Claims {
pub sub: String,
#[serde(default)]
pub tenant: Option<String>,
pub exp: usize,
}
#[derive(Debug, Clone)]
pub struct JwtResult {
pub user_id: Option<String>,
pub tenant: Option<String>,
}
impl JwtResult {
pub fn empty() -> Self {
Self {
user_id: None,
tenant: None,
}
}
pub fn is_authenticated(&self) -> bool {
self.user_id.is_some()
}
}
pub(crate) fn validate_jwt_with_config(token: &str, config: &Config) -> Result<Claims, String> {
let decoding_key = DecodingKey::from_secret(config.jwt.secret.as_ref());
let validation = Validation::default();
let decoded = decode::<Claims>(token, &decoding_key, &validation).map_err(|e| {
tracing::debug!(error = %e, "JWT decode failed");
"Invalid or expired token"
})?;
Ok(decoded.claims)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_jwt_result_empty() {
let result = JwtResult::empty();
assert!(!result.is_authenticated());
assert!(result.user_id.is_none());
assert!(result.tenant.is_none());
}
#[test]
fn test_jwt_result_authenticated() {
let result = JwtResult {
user_id: Some("123".to_string()),
tenant: Some("acme".to_string()),
};
assert!(result.is_authenticated());
}
}