systemprompt_security/jwt/
decode.rs1use std::collections::BTreeMap;
14use systemprompt_identifiers::{Actor, ClientId, SessionId, UserId};
15use systemprompt_models::auth::{Permission, UserType};
16
17use super::validate::{ValidationPolicy, decode_rs256_claims};
18use crate::error::{AuthError, AuthResult};
19
20#[derive(Debug, Clone)]
21pub struct JwtUserContext {
22 pub user_id: UserId,
23 pub session_id: SessionId,
24 pub role: Permission,
25 pub user_type: UserType,
26 pub client_id: Option<ClientId>,
27 pub act_chain: Vec<Actor>,
28 pub attributes: BTreeMap<String, serde_json::Value>,
29 pub jti: String,
30 pub exp: i64,
31}
32
33pub fn extract_user_context(token: &str) -> AuthResult<JwtUserContext> {
34 let claims = decode_rs256_claims(token, &ValidationPolicy::session_context())?;
35
36 let session_id = claims.session_id.ok_or(AuthError::MissingSessionId)?;
37 let role = *claims.scope.first().ok_or(AuthError::MissingScope)?;
38 let derived_type = UserType::from_permissions(&claims.scope);
39 if derived_type != claims.user_type {
40 return Err(AuthError::UserTypeMismatch {
41 claimed: claims.user_type,
42 derived: derived_type,
43 });
44 }
45 let act_chain = claims
46 .act
47 .as_ref()
48 .map(systemprompt_models::auth::ActClaim::flatten_to_chain)
49 .unwrap_or_default();
50
51 Ok(JwtUserContext {
52 user_id: UserId::new(claims.sub),
53 session_id,
54 role,
55 user_type: derived_type,
56 client_id: claims.client_id,
57 act_chain,
58 attributes: claims.attributes,
59 jti: claims.jti,
60 exp: claims.exp,
61 })
62}