Skip to main content

systemprompt_agent/services/shared/
auth.rs

1use crate::services::shared::error::{AgentServiceError, Result};
2use jsonwebtoken::{DecodingKey, Validation, decode};
3use systemprompt_identifiers::UserId;
4pub use systemprompt_models::auth::JwtClaims;
5use systemprompt_traits::AgentJwtClaims;
6
7pub struct JwtValidator {
8    decoding_key: DecodingKey,
9    validation: Validation,
10}
11
12impl std::fmt::Debug for JwtValidator {
13    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14        f.debug_struct("JwtValidator")
15            .field("validation", &self.validation)
16            .field("decoding_key", &"<decoding_key>")
17            .finish()
18    }
19}
20
21impl JwtValidator {
22    pub fn new(secret: &str) -> Self {
23        Self {
24            decoding_key: DecodingKey::from_secret(secret.as_bytes()),
25            validation: Validation::default(),
26        }
27    }
28
29    pub fn validate_token(&self, token: &str) -> Result<JwtClaims> {
30        decode::<JwtClaims>(token, &self.decoding_key, &self.validation)
31            .map(|data| data.claims)
32            .map_err(|e| AgentServiceError::Authentication(format!("invalid token: {e}")))
33    }
34}
35
36pub fn extract_bearer_token(authorization_header: &str) -> Result<&str> {
37    authorization_header.strip_prefix("Bearer ").ok_or_else(|| {
38        AgentServiceError::Authentication("invalid authorization header format".to_string())
39    })
40}
41
42#[derive(Debug, Clone)]
43pub struct AgentSessionUser {
44    pub id: UserId,
45    pub username: String,
46    pub user_type: String,
47    pub roles: Vec<String>,
48}
49
50impl AgentSessionUser {
51    pub fn from_jwt_claims(claims: AgentJwtClaims) -> Self {
52        Self {
53            id: UserId::new(claims.subject),
54            username: claims.username,
55            user_type: claims.user_type,
56            roles: claims.permissions,
57        }
58    }
59}
60
61impl From<JwtClaims> for AgentSessionUser {
62    fn from(claims: JwtClaims) -> Self {
63        Self {
64            id: UserId::new(claims.sub.clone()),
65            username: claims.username.clone(),
66            user_type: claims.user_type.to_string(),
67            roles: claims.get_scopes(),
68        }
69    }
70}