sinan 0.1.0

A Boilerplate for Rapid Axum Web Service Deployment.
Documentation
use axum::extract::Request;
use chrono::{Duration, Utc};
use jsonwebtoken::errors::Error;
use jsonwebtoken::{DecodingKey, EncodingKey, Header, Validation};
use once_cell::sync::Lazy;

pub use claims::JWTClaims;
use token::Token;

mod claims;
mod token;

pub struct JWT;

impl JWT {
    pub fn token(username: String) -> eyre::Result<Token> {
        let exp = Utc::now() + Duration::seconds(JWT_CONFIG.get_expire());

        let token = jsonwebtoken::encode(
            &Header::default(),
            &JWTClaims::new(
                None,
                exp.timestamp(),
                Some(Utc::now().timestamp()),
                None,
                None,
                Some(username),
            ),
            &EncodingKey::from_secret(JWT_CONFIG.get_secret()),
        )?;

        Ok(Token::new(token, JWT_CONFIG.get_expire() + 60)) // validation leeway
    }

    pub fn authorize(request: &Request) -> Result<JWTClaims, Error> {
        let token_data = jsonwebtoken::decode::<JWTClaims>(
            Token::parse_from_header(request.headers())?,
            &DecodingKey::from_secret(JWT_CONFIG.get_secret()),
            &Validation::default(),
        )?;

        Ok(token_data.claims)
    }
}

struct JwtConfig {
    secret: String,
    expire: i64,
}

impl JwtConfig {
    fn new(secret: String, expire: i64) -> Self {
        Self { secret, expire }
    }

    fn get_secret(&self) -> &[u8] {
        self.secret.as_bytes()
    }

    fn get_expire(&self) -> i64 {
        self.expire
    }
}

static JWT_CONFIG: Lazy<JwtConfig> = Lazy::new(|| {
    let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set");
    let expire = std::env::var("JWT_EXPIRE").expect("JWT_EXPIRE must be set");

    JwtConfig::new(secret, expire.parse().unwrap_or(3600))
});