use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "config-schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum UnsafeFrontendClientSecret {
#[default]
Disabled,
Enabled,
}
impl UnsafeFrontendClientSecret {
pub fn is_enabled(self) -> bool {
matches!(self, Self::Enabled)
}
pub fn warn_if_enabled(self) {
if self.is_enabled() {
tracing::warn!(
capability = "UnsafeFrontendClientSecret",
"⚠️ SECURITY WARNING: the frontend-oidc client_secret capability is ENABLED. The \
OIDC client secret will be exposed to the browser. This is a security \
anti-pattern and should only be used as a compatibility workaround for \
misconfigured OIDC providers."
);
}
}
}
#[cfg_attr(feature = "config-schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FrontendOidcModeCapabilities {
#[serde(default)]
pub unsafe_frontend_client_secret: UnsafeFrontendClientSecret,
}
impl FrontendOidcModeCapabilities {
pub fn warn_unsafe(&self) {
self.unsafe_frontend_client_secret.warn_if_enabled();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_capabilities_are_safe() {
let caps = FrontendOidcModeCapabilities::default();
assert!(!caps.unsafe_frontend_client_secret.is_enabled());
}
#[test]
fn enabled_capability_is_detected() {
let caps = FrontendOidcModeCapabilities {
unsafe_frontend_client_secret: UnsafeFrontendClientSecret::Enabled,
};
assert!(caps.unsafe_frontend_client_secret.is_enabled());
}
#[test]
fn deserialize_from_toml_style_json() {
let json = serde_json::json!({
"unsafe_frontend_client_secret": "enabled"
});
let caps: FrontendOidcModeCapabilities =
serde_json::from_value(json).expect("should deserialize");
assert!(caps.unsafe_frontend_client_secret.is_enabled());
}
#[test]
fn deserialize_defaults_to_disabled() {
let json = serde_json::json!({});
let caps: FrontendOidcModeCapabilities =
serde_json::from_value(json).expect("should deserialize");
assert!(!caps.unsafe_frontend_client_secret.is_enabled());
}
}