Skip to main content

systemprompt_agent/services/shared/
auth.rs

1//! JWT validation for agent requests: decoding bearer tokens into typed
2//! session claims and extracting the authenticated [`UserId`].
3//!
4//! Copyright (c) systemprompt.io — Business Source License 1.1.
5//! See <https://systemprompt.io> for licensing details.
6
7use crate::services::shared::error::{AgentServiceError, Result};
8use systemprompt_identifiers::UserId;
9pub use systemprompt_models::auth::JwtClaims;
10use systemprompt_security::jwt::{ValidationPolicy, decode_rs256_claims};
11use systemprompt_traits::AgentJwtClaims;
12
13#[derive(Debug, Default, Clone, Copy)]
14pub struct JwtValidator;
15
16impl JwtValidator {
17    #[must_use]
18    pub const fn new() -> Self {
19        Self
20    }
21
22    #[expect(
23        clippy::unused_self,
24        reason = "trait-shaped method kept on impl for symmetry"
25    )]
26    pub fn validate_token(&self, token: &str) -> Result<JwtClaims> {
27        decode_rs256_claims(token, &ValidationPolicy::session_context())
28            .map_err(|e| AgentServiceError::Authentication(e.to_string()))
29    }
30}
31
32pub fn extract_bearer_token(authorization_header: &str) -> Result<&str> {
33    authorization_header.strip_prefix("Bearer ").ok_or_else(|| {
34        AgentServiceError::Authentication("invalid authorization header format".to_owned())
35    })
36}
37
38#[derive(Debug, Clone)]
39pub struct AgentSessionUser {
40    pub id: UserId,
41    pub username: String,
42    pub user_type: String,
43    pub permissions: Vec<String>,
44}
45
46impl AgentSessionUser {
47    pub fn from_jwt_claims(claims: AgentJwtClaims) -> Self {
48        Self {
49            id: UserId::new(claims.subject),
50            username: claims.username,
51            user_type: claims.user_type,
52            permissions: claims.permissions,
53        }
54    }
55}