125 lines
3.6 KiB
Rust

use crate::config::Config;
use jwt_simple::common::VerificationOptions;
use jwt_simple::prelude::*;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use tokio::fs;
#[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<Self, String> {
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<String> = HashSet::new();
issuers.insert(self.issuer.clone());
verification_options.allowed_issuers = Some(issuers);
verification_options
}
/// builds and signs the token
pub fn sign(&self, email: String) -> Result<String, String> {
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 },
Duration::from_hours(self.exp_time),
);
claims.issuer = Some(self.issuer.clone());
match jwt_key.sign(claims) {
Ok(token) => Ok(token),
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::<NoCustomClaims>(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());
}