Skip to main content

systemprompt_models/profile/
security.rs

1//! Profile `security:` block: signing keys, trusted issuers, resource
2//! audiences.
3//!
4//! Copyright (c) systemprompt.io — Business Source License 1.1.
5//! See <https://systemprompt.io> for licensing details.
6
7use std::path::PathBuf;
8
9use crate::auth::JwtAudience;
10use serde::{Deserialize, Serialize};
11
12pub const GATEWAY_REQUIRED_RESOURCE_AUDIENCES: &[&str] = &["hook"];
13
14#[must_use]
15pub fn default_resource_audiences() -> Vec<String> {
16    GATEWAY_REQUIRED_RESOURCE_AUDIENCES
17        .iter()
18        .map(|aud| (*aud).to_owned())
19        .collect()
20}
21
22const fn default_allow_registration() -> bool {
23    true
24}
25
26fn default_signing_key_path() -> PathBuf {
27    PathBuf::from("signing_key.pem")
28}
29
30pub const DEFAULT_ID_JAG_TTL_SECS: i64 = 300;
31
32const fn default_id_jag_ttl_secs() -> i64 {
33    DEFAULT_ID_JAG_TTL_SECS
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
37#[serde(deny_unknown_fields)]
38pub struct SecurityConfig {
39    #[serde(rename = "jwt_issuer")]
40    pub issuer: String,
41
42    #[serde(rename = "jwt_access_token_expiration")]
43    pub access_token_expiration: i64,
44
45    #[serde(rename = "jwt_refresh_token_expiration")]
46    pub refresh_token_expiration: i64,
47
48    #[serde(rename = "jwt_audiences")]
49    pub audiences: Vec<JwtAudience>,
50
51    #[serde(default)]
52    pub allowed_resource_audiences: Vec<String>,
53
54    #[serde(default = "default_allow_registration")]
55    pub allow_registration: bool,
56
57    // Why: when set, the OAuth authorize endpoint 302s to this
58    // deployment-owned sign-in page (carrying the original query) instead of
59    // rendering the built-in WebAuthn form; prompt=passkey opts back in.
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub login_page_url: Option<String>,
62
63    #[serde(default = "default_signing_key_path")]
64    pub signing_key_path: PathBuf,
65
66    #[serde(default, skip_serializing_if = "Vec::is_empty")]
67    pub trusted_issuers: Vec<TrustedIssuer>,
68
69    #[serde(default = "default_id_jag_ttl_secs")]
70    pub id_jag_ttl_secs: i64,
71}
72
73/// A federated identity provider trusted for the RFC 8693 token-exchange and
74/// EMA (Enterprise-Managed Authorization) paths.
75///
76/// `audience` holds the value the `IdP` places in `id_token.aud`; for a
77/// Salesforce Connected App that is its `client_id`, **not** a URL.
78#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)]
79#[serde(deny_unknown_fields)]
80pub struct TrustedIssuer {
81    pub issuer: String,
82    pub jwks_uri: String,
83    pub audience: String,
84
85    #[serde(default, skip_serializing_if = "Vec::is_empty")]
86    pub typ_allowlist: Vec<String>,
87
88    #[serde(default, skip_serializing_if = "Vec::is_empty")]
89    pub allowed_client_ids: Vec<String>,
90
91    #[serde(default)]
92    pub can_issue_id_jag: bool,
93}