Skip to main content

systemprompt_security/session/
generator.rs

1use chrono::{Duration, Utc};
2use jsonwebtoken::{Algorithm, Header, encode};
3use std::collections::BTreeMap;
4use systemprompt_identifiers::{SessionId, SessionToken, UserId};
5use systemprompt_models::auth::{
6    JwtAudience, JwtClaims, Permission, RateLimitTier, TokenType, UserType,
7};
8
9use crate::error::{JwtError, JwtResult};
10use crate::keys::authority;
11
12#[derive(Debug)]
13pub struct SessionParams<'a> {
14    pub user_id: &'a UserId,
15    pub session_id: &'a SessionId,
16    pub email: &'a str,
17    pub duration: Duration,
18    pub user_type: UserType,
19    pub permissions: Vec<Permission>,
20    pub roles: Vec<String>,
21    pub attributes: BTreeMap<String, serde_json::Value>,
22    pub rate_limit_tier: RateLimitTier,
23}
24
25#[derive(Debug)]
26pub struct SessionGenerator {
27    issuer: String,
28}
29
30impl SessionGenerator {
31    pub fn new(issuer: impl Into<String>) -> Self {
32        Self {
33            issuer: issuer.into(),
34        }
35    }
36
37    pub fn generate(&self, params: &SessionParams<'_>) -> JwtResult<SessionToken> {
38        let now = Utc::now();
39        let expiry = now + params.duration;
40
41        let claims = JwtClaims {
42            sub: params.user_id.to_string(),
43            iat: now.timestamp(),
44            exp: expiry.timestamp(),
45            nbf: Some(now.timestamp()),
46            iss: self.issuer.clone(),
47            aud: JwtAudience::standard(),
48            jti: uuid::Uuid::new_v4().to_string(),
49            scope: params.permissions.clone(),
50            username: params.email.to_owned(),
51            email: params.email.to_owned(),
52            user_type: params.user_type,
53            roles: params.roles.clone(),
54            attributes: params.attributes.clone(),
55            client_id: None,
56            token_type: TokenType::Bearer,
57            auth_time: now.timestamp(),
58            session_id: Some(params.session_id.clone()),
59            rate_limit_tier: Some(params.rate_limit_tier),
60            plugin_id: None,
61            act: None,
62        };
63
64        let kid = authority::active_kid().map_err(|e| JwtError::Signing(e.to_string()))?;
65        let mut header = Header::new(Algorithm::RS256);
66        header.kid = Some(kid.to_owned());
67        let key = authority::encoding_key().map_err(|e| JwtError::Signing(e.to_string()))?;
68        let token = encode(&header, &claims, key).map_err(JwtError::from)?;
69
70        Ok(SessionToken::new(token))
71    }
72}