Skip to main content

fraiseql_server/server_config/
hs256.rs

1//! HS256 symmetric-key authentication configuration.
2//!
3//! HS256 auth is an alternative to OIDC for integration testing and internal
4//! service-to-service scenarios where a shared secret is acceptable. Unlike
5//! OIDC, validation is fully local — no discovery endpoint, no JWKS fetch.
6//!
7//! For public-facing production, prefer OIDC (`[auth]`).
8
9use serde::{Deserialize, Serialize};
10
11/// HS256 authentication configuration.
12///
13/// Loaded from the `[auth_hs256]` section of `fraiseql.toml`. Mutually
14/// exclusive with `[auth]` (OIDC).
15///
16/// # Example (TOML)
17///
18/// ```toml
19/// [auth_hs256]
20/// secret_env = "FRAISEQL_HS256_SECRET"
21/// issuer = "my-test-suite"
22/// audience = "my-api"
23/// ```
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct Hs256Config {
26    /// Name of the environment variable holding the shared secret.
27    ///
28    /// The secret itself is never stored in the config file. At server
29    /// startup, the value of this environment variable is used as the HS256
30    /// signing key.
31    pub secret_env: String,
32
33    /// Expected `iss` claim (optional).
34    #[serde(default)]
35    pub issuer: Option<String>,
36
37    /// Expected `aud` claim (optional).
38    #[serde(default)]
39    pub audience: Option<String>,
40}
41
42impl Hs256Config {
43    /// Resolve the shared secret from the configured environment variable.
44    ///
45    /// # Errors
46    ///
47    /// Returns an error string when the environment variable is unset or empty.
48    pub fn load_secret(&self) -> Result<String, String> {
49        let value = std::env::var(&self.secret_env).map_err(|_| {
50            format!("auth_hs256: environment variable `{}` is not set", self.secret_env)
51        })?;
52        if value.is_empty() {
53            return Err(format!("auth_hs256: environment variable `{}` is empty", self.secret_env));
54        }
55        Ok(value)
56    }
57}