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