use crate::error::AuthError;
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use password_auth::verify_password;
use rand::distr::Alphanumeric;
use rand::Rng;
use scouter_sql::sql::schema::User;
use serde::{Deserialize, Serialize};
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
pub sub: String,
exp: usize,
pub permissions: Vec<String>,
pub group_permissions: Vec<String>,
salt: String,
}
pub struct AuthManager {
jwt_secret: String,
refresh_secret: String,
}
impl AuthManager {
pub fn new(jwt_secret: &str, refresh_secret: &str) -> Self {
Self {
jwt_secret: jwt_secret.to_string(),
refresh_secret: refresh_secret.to_string(),
}
}
fn generate_salt(&self) -> String {
rand::rng()
.sample_iter(&Alphanumeric)
.take(16)
.map(char::from)
.collect()
}
pub fn generate_jwt(&self, user: &User) -> String {
let expiration = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ 3600;
let claims = Claims {
sub: user.username.clone(),
exp: expiration as usize,
permissions: user.permissions.clone(),
group_permissions: user.group_permissions.clone(),
salt: self.generate_salt(),
};
encode(
&Header::default(),
&claims,
&EncodingKey::from_secret(self.jwt_secret.as_ref()),
)
.unwrap()
}
pub fn generate_refresh_token(&self, user: &User) -> String {
let expiration = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ 86400;
let claims = Claims {
sub: user.username.clone(),
exp: expiration as usize,
permissions: user.permissions.clone(),
group_permissions: user.group_permissions.clone(),
salt: self.generate_salt(),
};
encode(
&Header::default(),
&claims,
&EncodingKey::from_secret(self.refresh_secret.as_ref()),
)
.unwrap()
}
pub fn validate_jwt(&self, token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
let token_data = decode::<Claims>(
token,
&DecodingKey::from_secret(self.jwt_secret.as_ref()),
&Validation::default(),
)?;
Ok(token_data.claims)
}
pub fn decode_jwt_without_validation(
&self,
token: &str,
) -> Result<Claims, jsonwebtoken::errors::Error> {
let mut validation = Validation::default();
validation.insecure_disable_signature_validation();
let token_data = decode::<Claims>(
token,
&DecodingKey::from_secret(self.jwt_secret.as_ref()),
&validation,
)?;
Ok(token_data.claims)
}
pub fn validate_refresh_token(
&self,
token: &str,
) -> Result<Claims, jsonwebtoken::errors::Error> {
let token_data = decode::<Claims>(
token,
&DecodingKey::from_secret(self.refresh_secret.as_ref()),
&Validation::default(),
)?;
Ok(token_data.claims)
}
pub fn validate_user(&self, user: &User, password: &str) -> Result<(), AuthError> {
verify_password(password, &user.password_hash).map_err(AuthError::InvalidPassword)
}
}