//! JWT token utilities for HMAC-SHA256 / HS256 signing and verification. use serde::{Deserialize, Serialize}; /// Standard JWT claims with optional session binding. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct JwtClaims { pub sub: String, pub exp: u64, pub iat: u64, #[serde(skip_serializing_if = "Option::is_none")] pub session_id: Option, } impl JwtClaims { pub fn new(sub: String, exp: u64, session_id: Option) -> Self { let iat = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap_or_default() .as_secs(); Self { sub, exp, iat, session_id, } } } /// Sign a set of claims into a JWT string using HS256. pub fn create_token(secret: &str, claims: JwtClaims) -> anyhow::Result { let header = jsonwebtoken::Header::new(jsonwebtoken::Algorithm::HS256); let key = jsonwebtoken::EncodingKey::from_secret(secret.as_bytes()); let token = jsonwebtoken::encode(&header, &claims, &key)?; Ok(token) } /// Verify a JWT string and return its claims. pub fn verify_token(secret: &str, token: &str) -> anyhow::Result { let mut validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::HS256); validation.validate_exp = true; validation.required_spec_claims = ["sub", "exp", "iat"] .iter() .map(|&s| s.to_string()) .collect(); let key = jsonwebtoken::DecodingKey::from_secret(secret.as_bytes()); let token_data = jsonwebtoken::decode::(token, &key, &validation)?; Ok(token_data.claims) }