use crate::config::Config; use jwt_simple::common::VerificationOptions; use jwt_simple::prelude::*; use serde::{Deserialize, Serialize}; use std::collections::HashSet; use tokio::fs; use crate::message::JWTMessage; use crate::stores::Credentials; #[derive(Serialize, Deserialize)] struct JWTCustomClaims { email: String, } pub struct JWTSigner { private_key: String, public_key: String, issuer: String, exp_time: u64, } impl JWTSigner { // NOTE: could be included in a Trait: `TryFrom` but difficult to handle with async pub async fn new(config: Config) -> Result { let mut jwt_signer = JWTSigner { private_key: "".to_string(), public_key: "".to_string(), issuer: config.jwt_issuer, exp_time: config.jwt_exp_time, }; match fs::read_to_string(config.jwt_priv_key).await { Ok(c) => { jwt_signer.private_key = c; } Err(e) => { return Err(format!("unable to read the private key details={}", e)); } } match fs::read_to_string(config.jwt_pub_key).await { Ok(c) => { jwt_signer.public_key = c; } Err(e) => { return Err(format!("unable to read the public key details={}", e)); } } Ok(jwt_signer) } fn get_verification_options(&self) -> VerificationOptions { let mut verification_options = VerificationOptions::default(); let mut issuers: HashSet = HashSet::new(); issuers.insert(self.issuer.clone()); verification_options.allowed_issuers = Some(issuers); verification_options } /// sign builds and signs the token pub fn sign(&self, credentials: Credentials) -> Result { let jwt_key = { match RS384KeyPair::from_pem(&self.private_key) { Ok(k) => k, Err(e) => { return Err(format!("unable to load the private key details={}", e)); } } }; let mut claims = Claims::with_custom_claims( JWTCustomClaims { email: credentials.get_email(), }, Duration::from_hours(self.exp_time), ); claims.issuer = Some(self.issuer.clone()); match jwt_key.sign(claims) { Ok(token) => { // TODO: need to generate the refresh token return Ok(serde_json::to_string(&JWTMessage::with_access(token)).unwrap()); } Err(e) => { return Err(format!("unable to sign the token details={}", e)); } } } pub fn validate(&self, token: &str) -> Result<(), String> { let verification_options = self.get_verification_options(); match RS384PublicKey::from_pem(&self.public_key) { Ok(key) => { if let Err(e) = key.verify_token::(token, Some(verification_options)) { return Err(format!("token validation failed details={}", e)); } Ok(()) } Err(e) => Err(format!( "token validation failed, can't read the public key details={}", e )), } } pub fn get_public_key(&self) -> String { self.public_key.clone() } } #[tokio::test] async fn test_signer() { use configparser::ini::Ini; use std::env; let root_path = env::var("CARGO_MANIFEST_DIR").unwrap(); let config_path = format!("{}/{}/{}/{}", root_path, "tests", "data", "config.ini"); let mut config = Ini::new(); let _r = config.load(config_path); let router_config = Config::try_from(config); assert!(router_config.is_ok()); let jwt_signer = JWTSigner::new(router_config.unwrap()); assert!(jwt_signer.await.is_ok()); }