scouter_auth/
auth.rs

1use crate::error::AuthError;
2use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
3use password_auth::verify_password;
4use rand::distr::Alphanumeric;
5use rand::Rng;
6use scouter_sql::sql::schema::User;
7use serde::{Deserialize, Serialize};
8use std::time::{SystemTime, UNIX_EPOCH};
9
10#[derive(Debug, Serialize, Deserialize)]
11pub struct Claims {
12    pub sub: String,
13    exp: usize,
14    pub permissions: Vec<String>,
15    pub group_permissions: Vec<String>,
16    salt: String,
17}
18
19pub struct AuthManager {
20    jwt_secret: String,
21    refresh_secret: String,
22}
23impl AuthManager {
24    pub fn new(jwt_secret: &str, refresh_secret: &str) -> Self {
25        Self {
26            jwt_secret: jwt_secret.to_string(),
27            refresh_secret: refresh_secret.to_string(),
28        }
29    }
30
31    fn generate_salt(&self) -> String {
32        rand::rng()
33            .sample_iter(&Alphanumeric)
34            .take(16)
35            .map(char::from)
36            .collect()
37    }
38
39    pub fn generate_jwt(&self, user: &User) -> String {
40        let expiration = SystemTime::now()
41            .duration_since(UNIX_EPOCH)
42            .unwrap()
43            .as_secs()
44            + 3600; // 1 hour expiration
45
46        let claims = Claims {
47            sub: user.username.clone(),
48            exp: expiration as usize,
49            permissions: user.permissions.clone(),
50            group_permissions: user.group_permissions.clone(),
51            salt: self.generate_salt(),
52        };
53
54        encode(
55            &Header::default(),
56            &claims,
57            &EncodingKey::from_secret(self.jwt_secret.as_ref()),
58        )
59        .unwrap()
60    }
61
62    pub fn generate_refresh_token(&self, user: &User) -> String {
63        let expiration = SystemTime::now()
64            .duration_since(UNIX_EPOCH)
65            .unwrap()
66            .as_secs()
67            + 86400; // 24 hours expiration
68
69        let claims = Claims {
70            sub: user.username.clone(),
71            exp: expiration as usize,
72            permissions: user.permissions.clone(),
73            group_permissions: user.group_permissions.clone(),
74            salt: self.generate_salt(),
75        };
76
77        encode(
78            &Header::default(),
79            &claims,
80            &EncodingKey::from_secret(self.refresh_secret.as_ref()),
81        )
82        .unwrap()
83    }
84
85    pub fn validate_jwt(&self, token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
86        let token_data = decode::<Claims>(
87            token,
88            &DecodingKey::from_secret(self.jwt_secret.as_ref()),
89            &Validation::default(),
90        )?;
91        Ok(token_data.claims)
92    }
93
94    pub fn decode_jwt_without_validation(
95        &self,
96        token: &str,
97    ) -> Result<Claims, jsonwebtoken::errors::Error> {
98        let mut validation = Validation::default();
99        validation.insecure_disable_signature_validation();
100        let token_data = decode::<Claims>(
101            token,
102            &DecodingKey::from_secret(self.jwt_secret.as_ref()),
103            &validation,
104        )?;
105
106        Ok(token_data.claims)
107    }
108
109    pub fn validate_refresh_token(
110        &self,
111        token: &str,
112    ) -> Result<Claims, jsonwebtoken::errors::Error> {
113        let token_data = decode::<Claims>(
114            token,
115            &DecodingKey::from_secret(self.refresh_secret.as_ref()),
116            &Validation::default(),
117        )?;
118        Ok(token_data.claims)
119    }
120
121    pub fn validate_user(&self, user: &User, password: &str) -> Result<(), AuthError> {
122        verify_password(password, &user.password_hash).map_err(AuthError::InvalidPassword)
123    }
124}