use axum::{Json, http::StatusCode}; use jsonwebtoken::{DecodingKey, EncodingKey, Header, decode, encode}; use serde::{Deserialize, Serialize}; use crate::models::Claims; /// Error response for JWT token operations. /// /// Returned when token encoding or decoding fails. Used in error responses /// for invalid or expired tokens. /// /// # Fields /// - `status`: HTTP status text (e.g., "error") /// - `message`: Human-readable error description #[derive(Debug, Deserialize, Serialize)] pub struct Error { pub status: &'static str, pub message: String, } /// Encodes user information into a JSON Web Token (JWT). /// /// This function creates a new JWT with the provided user ID as the subject, /// sets the issued-at and expiration times (60 minutes from now), and signs it /// using the given encoding key. /// /// # Arguments /// - `header`: The JWT header, specifying the algorithm (e.g., HS256). /// - `id`: The user ID (`String`) to be embedded as the subject (`sub`) claim. /// - `key`: The `EncodingKey` used to sign the JWT. /// /// # Returns /// A `String` representing the encoded JWT. /// /// # Panics /// Panics if the token encoding fails for any reason (e.g., invalid key). pub fn encode_token(header: &Header, id: String, key: &EncodingKey) -> String { let now = chrono::Utc::now(); let expires = (now + chrono::Duration::minutes(60)).timestamp(); let claims: Claims = Claims { sub: id, issued: now.timestamp() as usize, expires: expires as usize, }; let token = encode(header, &claims, key); return token.expect("token return failed"); } /// Decodes and validates a JSON Web Token (JWT). /// /// This function attempts to decode a JWT string, validate its signature and claims /// using the provided decoding key. It specifically ignores expiration (`validate_exp`) /// and "not before" (`validate_nbf`) claims during validation. /// /// # Arguments /// - `token`: The JWT string to decode. /// - `key`: The `DecodingKey` used to verify the JWT's signature. /// /// # Returns /// - `200 OK`: If the token is successfully decoded and verified, returns the extracted `Claims`. /// - `401 UNAUTHORIZED`: If the token is invalid, expired, or cannot be decoded, /// returns an `UNAUTHORIZED` status code along with a JSON error message. pub fn decode_token(token: String, key: &DecodingKey) -> Result)> { let mut validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::HS256); validation.validate_exp = false; validation.validate_nbf = false; validation.leeway = 0; let claims = decode::(&token, key, &validation) .map_err(|err| { let message = format!("Invalid Token: {}", err); let error = Error { status: "error", message, }; (StatusCode::UNAUTHORIZED, Json(error)) })? .claims; return Ok(claims); }