Skip to main content

systemprompt_models/profile/
security.rs

1use std::path::PathBuf;
2
3use crate::auth::JwtAudience;
4use serde::{Deserialize, Serialize};
5
6/// Audiences the gateway's grant paths require to be present in
7/// [`SecurityConfig::allowed_resource_audiences`].
8///
9/// These are not RFC 8707 external resource URIs — they are the gateway's own
10/// internal protocol audiences that hardcoded scope guards depend on. The
11/// `client_credentials` grant rejects any `hook:*` scope that is not paired
12/// with `audience=hook`, so a profile that does not opt into the `"hook"`
13/// audience cannot mint plugin hook tokens for the bridge. Profile validation
14/// rejects bootstrap if any entry here is missing, so the error surfaces at
15/// the operator's YAML edit rather than at a downstream tenant's first call.
16pub const GATEWAY_REQUIRED_RESOURCE_AUDIENCES: &[&str] = &["hook"];
17
18/// The resource audiences every generated profile must opt into.
19///
20/// Returns [`GATEWAY_REQUIRED_RESOURCE_AUDIENCES`] as owned strings, so the
21/// setup wizard and the env-driven cloud bootstrap seed the same audiences and
22/// pass [`crate::profile::Profile::validate`] from one source of truth.
23#[must_use]
24pub fn default_resource_audiences() -> Vec<String> {
25    GATEWAY_REQUIRED_RESOURCE_AUDIENCES
26        .iter()
27        .map(|aud| (*aud).to_owned())
28        .collect()
29}
30
31const fn default_allow_registration() -> bool {
32    true
33}
34
35fn default_signing_key_path() -> PathBuf {
36    PathBuf::from("signing_key.pem")
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
40#[serde(deny_unknown_fields)]
41pub struct SecurityConfig {
42    #[serde(rename = "jwt_issuer")]
43    pub issuer: String,
44
45    #[serde(rename = "jwt_access_token_expiration")]
46    pub access_token_expiration: i64,
47
48    #[serde(rename = "jwt_refresh_token_expiration")]
49    pub refresh_token_expiration: i64,
50
51    #[serde(rename = "jwt_audiences")]
52    pub audiences: Vec<JwtAudience>,
53
54    #[serde(default)]
55    pub allowed_resource_audiences: Vec<String>,
56
57    #[serde(default = "default_allow_registration")]
58    pub allow_registration: bool,
59
60    #[serde(default = "default_signing_key_path")]
61    pub signing_key_path: PathBuf,
62
63    #[serde(default, skip_serializing_if = "Vec::is_empty")]
64    pub trusted_issuers: Vec<TrustedIssuer>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)]
68#[serde(deny_unknown_fields)]
69pub struct TrustedIssuer {
70    pub issuer: String,
71    pub jwks_uri: String,
72    pub audience: String,
73}