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}