use jsonwebtoken::{DecodingKey, EncodingKey, Header, Validation, decode, encode}; use uuid::Uuid; use crate::claims::{AccessClaims, RefreshClaims}; use crate::error::JwtError; pub fn algorithm_from_str(s: &str) -> Result { match s { "EdDSA" | "ed25519" | "Ed25519" => Ok(jsonwebtoken::Algorithm::EdDSA), "ES256" => Ok(jsonwebtoken::Algorithm::ES256), "RS256" => Ok(jsonwebtoken::Algorithm::RS256), other => Err(JwtError::Config(format!("unsupported algorithm: {other}"))), } } pub fn encode_access_token( claims: &AccessClaims, key: &EncodingKey, alg: jsonwebtoken::Algorithm, ) -> Result { let mut header = Header::new(alg); header.kid = Some(claims.kid.clone()); encode(&header, claims, key).map_err(JwtError::from) } pub fn encode_refresh_token( claims: &RefreshClaims, key: &EncodingKey, alg: jsonwebtoken::Algorithm, ) -> Result { let mut header = Header::new(alg); header.kid = Some(claims.kid.clone()); encode(&header, claims, key).map_err(JwtError::from) } pub fn decode_access_token( token: &str, key: &DecodingKey, alg: jsonwebtoken::Algorithm, issuer: &str, audience: &str, clock_skew_seconds: i64, ) -> Result { let mut validation = Validation::new(alg); validation.set_issuer(&[issuer.to_string()]); validation.set_audience(&[audience.to_string()]); validation.leeway = clock_skew_seconds as u64; validation.validate_exp = true; let data = decode::(token, key, &validation)?; Ok(data.claims) } pub fn decode_refresh_token( token: &str, key: &DecodingKey, alg: jsonwebtoken::Algorithm, issuer: &str, audience: &str, clock_skew_seconds: i64, ) -> Result { let mut validation = Validation::new(alg); validation.set_issuer(&[issuer.to_string()]); validation.set_audience(&[audience.to_string()]); validation.leeway = clock_skew_seconds as u64; validation.validate_exp = true; let data = decode::(token, key, &validation)?; Ok(data.claims) } pub fn kid_from_token(token: &str) -> Option { // Best-effort: decode only the header, ignore signature. let header = jsonwebtoken::decode_header(token).ok()?; header.kid } pub fn extract_kid(token: &str) -> Option { kid_from_token(token).and_then(|k| Uuid::parse_str(&k).ok()) }