Skip to main content

systemprompt_security/jwt/
mod.rs

1//! JWT minting service.
2//!
3//! Produces administrator-scoped HS256 tokens for the bridge management
4//! plane and CLI bootstrap flows. Session-scoped tokens are minted by
5//! [`crate::session::SessionGenerator`] instead.
6
7use chrono::{Duration, Utc};
8use jsonwebtoken::{Algorithm, EncodingKey, Header, encode};
9use systemprompt_identifiers::{ClientId, JwtToken, SessionId, UserId};
10use systemprompt_models::auth::{
11    JwtAudience, JwtClaims, Permission, RateLimitTier, TokenType, UserType,
12};
13
14use crate::error::{JwtError, JwtResult};
15
16#[derive(Debug)]
17pub struct AdminTokenParams<'a> {
18    pub user_id: &'a UserId,
19    pub session_id: &'a SessionId,
20    pub email: &'a str,
21    pub jwt_secret: &'a str,
22    pub issuer: &'a str,
23    pub duration: Duration,
24    pub client_id: Option<&'a ClientId>,
25}
26
27#[derive(Copy, Clone, Debug)]
28pub struct JwtService;
29
30impl JwtService {
31    pub fn generate_admin_token(params: &AdminTokenParams<'_>) -> JwtResult<JwtToken> {
32        let now = Utc::now();
33        let expiry = now + params.duration;
34
35        let claims = JwtClaims {
36            sub: params.user_id.to_string(),
37            iat: now.timestamp(),
38            exp: expiry.timestamp(),
39            iss: params.issuer.to_string(),
40            aud: JwtAudience::standard(),
41            jti: uuid::Uuid::new_v4().to_string(),
42            scope: vec![Permission::Admin],
43            username: params.email.to_string(),
44            email: params.email.to_string(),
45            user_type: UserType::Admin,
46            roles: vec!["admin".to_string(), "user".to_string()],
47            department: None,
48            client_id: params.client_id.map(ToString::to_string),
49            token_type: TokenType::Bearer,
50            auth_time: now.timestamp(),
51            session_id: Some(params.session_id.to_string()),
52            rate_limit_tier: Some(RateLimitTier::Admin),
53            plugin_id: None,
54        };
55
56        let header = Header::new(Algorithm::HS256);
57        let token = encode(
58            &header,
59            &claims,
60            &EncodingKey::from_secret(params.jwt_secret.as_bytes()),
61        )
62        .map_err(JwtError::from)?;
63
64        Ok(JwtToken::new(token))
65    }
66}