Skip to main content

systemprompt_api/services/static_content/
session.rs

1//! Resolves or provisions a browser `SessionInfo` from JWT cookies for
2//! static-content requests.
3//!
4//! Copyright (c) systemprompt.io — Business Source License 1.1.
5//! See <https://systemprompt.io> for licensing details.
6
7use std::sync::Arc;
8
9use std::net::IpAddr;
10
11use anyhow::Result;
12use axum::http::HeaderMap;
13use systemprompt_identifiers::{ClientId, JwtToken, SessionId, SessionSource, UserId};
14use systemprompt_oauth::{CreateAnonymousSessionInput, SessionCreationService, validate_jwt_token};
15use systemprompt_runtime::AppContext;
16use systemprompt_security::TokenExtractor;
17use systemprompt_traits::ExtractSignals;
18use systemprompt_users::UserService;
19
20#[derive(Debug, Clone)]
21pub struct SessionInfo {
22    pub session_id: SessionId,
23    pub user_id: UserId,
24    pub is_new: bool,
25    pub jwt_token: Option<JwtToken>,
26}
27
28pub async fn ensure_session(
29    headers: &HeaderMap,
30    uri: Option<&http::Uri>,
31    caller_ip: Option<IpAddr>,
32    ctx: &AppContext,
33) -> Result<SessionInfo> {
34    let config = systemprompt_manifest::Config::get()?;
35
36    if let Ok(token) = TokenExtractor::browser_only().extract(headers)
37        && let Ok(claims) = validate_jwt_token(&token, &config.jwt_issuer, &config.jwt_audiences)
38        && let Some(session_id) = claims.session_id
39        && let Ok(user_id) = UserId::try_new(claims.sub)
40    {
41        return Ok(SessionInfo {
42            session_id: SessionId::new(session_id),
43            user_id,
44            is_new: false,
45            jwt_token: Some(JwtToken::new(token)),
46        });
47    }
48
49    let user_service = UserService::new(Arc::clone(ctx.user_repository()));
50    let concrete = Arc::clone(&ctx.analytics_repositories().session_store);
51    let analytics: Arc<dyn systemprompt_traits::SessionProvider> = concrete;
52    let session_service = SessionCreationService::new(analytics, Arc::new(user_service));
53
54    let client_id = ClientId::new("sp_web");
55    let request_analytics = ctx
56        .analytics_service()
57        .extract_analytics(headers, ExtractSignals { uri, caller_ip });
58    let session_info = session_service
59        .create_anonymous_session(CreateAnonymousSessionInput {
60            analytics: &request_analytics,
61            client_id: &client_id,
62            session_source: SessionSource::Web,
63        })
64        .await?;
65
66    Ok(SessionInfo {
67        session_id: session_info.session_id,
68        user_id: session_info.user_id,
69        is_new: session_info.is_new,
70        jwt_token: Some(session_info.jwt_token),
71    })
72}