feat: enhance auth config (#922)

* feat: enhance auth config
This commit is contained in:
gudnuf
2025-08-05 04:55:17 -07:00
committed by GitHub
parent 2bd97e4d80
commit cceea654fe
6 changed files with 195 additions and 143 deletions

View File

@@ -17,6 +17,7 @@ use anyhow::Result;
use bip39::Mnemonic; use bip39::Mnemonic;
use cdk_integration_tests::cli::CommonArgs; use cdk_integration_tests::cli::CommonArgs;
use cdk_integration_tests::shared; use cdk_integration_tests::shared;
use cdk_mintd::config::AuthType;
use clap::Parser; use clap::Parser;
use tokio::sync::Notify; use tokio::sync::Notify;
@@ -68,16 +69,19 @@ async fn start_fake_auth_mint(
// Enable authentication // Enable authentication
settings.auth = Some(cdk_mintd::config::Auth { settings.auth = Some(cdk_mintd::config::Auth {
auth_enabled: true,
openid_discovery, openid_discovery,
openid_client_id: "cashu-client".to_string(), openid_client_id: "cashu-client".to_string(),
mint_max_bat: 50, mint_max_bat: 50,
enabled_mint: true, mint: AuthType::Blind,
enabled_melt: true, get_mint_quote: AuthType::Blind,
enabled_swap: true, check_mint_quote: AuthType::Blind,
enabled_check_mint_quote: true, melt: AuthType::Blind,
enabled_check_melt_quote: true, get_melt_quote: AuthType::Blind,
enabled_restore: true, check_melt_quote: AuthType::Blind,
enabled_check_proof_state: true, swap: AuthType::Blind,
restore: AuthType::Blind,
check_proof_state: AuthType::Blind,
}); });
// Set description for the mint // Set description for the mint

View File

@@ -83,13 +83,23 @@ reserve_fee_min = 4
# tls_dir = "/path/to/tls" # tls_dir = "/path/to/tls"
# [auth] # [auth]
# Set to true to enable authentication features (defaults to false)
# auth_enabled = false
# openid_discovery = "http://127.0.0.1:8080/realms/cdk-test-realm/.well-known/openid-configuration" # openid_discovery = "http://127.0.0.1:8080/realms/cdk-test-realm/.well-known/openid-configuration"
# openid_client_id = "cashu-client" # openid_client_id = "cashu-client"
# mint_max_bat=50 # mint_max_bat=50
# enabled_mint=true
# enabled_melt=true # Authentication settings for endpoints
# enabled_swap=true # Options: "clear", "blind", "none" (none = disabled)
# enabled_check_mint_quote=true
# enabled_check_melt_quote=true # mint = "blind"
# enabled_restore=true # get_mint_quote = "none"
# enabled_check_proof_state=true # check_mint_quote = "none"
# melt = "none"
# get_melt_quote = "none"
# check_melt_quote = "none"
# swap = "blind"
# restore = "blind"
# check_proof_state = "none"

View File

@@ -208,30 +208,59 @@ pub struct Database {
pub engine: DatabaseEngine, pub engine: DatabaseEngine,
} }
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "lowercase")]
pub enum AuthType {
Clear,
Blind,
#[default]
None,
}
impl std::str::FromStr for AuthType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"clear" => Ok(AuthType::Clear),
"blind" => Ok(AuthType::Blind),
"none" => Ok(AuthType::None),
_ => Err(format!("Unknown auth type: {s}")),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Auth { pub struct Auth {
#[serde(default)]
pub auth_enabled: bool,
pub openid_discovery: String, pub openid_discovery: String,
pub openid_client_id: String, pub openid_client_id: String,
pub mint_max_bat: u64, pub mint_max_bat: u64,
#[serde(default = "default_true")] #[serde(default = "default_blind")]
pub enabled_mint: bool, pub mint: AuthType,
#[serde(default = "default_true")] #[serde(default)]
pub enabled_melt: bool, pub get_mint_quote: AuthType,
#[serde(default = "default_true")] #[serde(default)]
pub enabled_swap: bool, pub check_mint_quote: AuthType,
#[serde(default = "default_true")] #[serde(default)]
pub enabled_check_mint_quote: bool, pub melt: AuthType,
#[serde(default = "default_true")] #[serde(default)]
pub enabled_check_melt_quote: bool, pub get_melt_quote: AuthType,
#[serde(default = "default_true")] #[serde(default)]
pub enabled_restore: bool, pub check_melt_quote: AuthType,
#[serde(default = "default_true")] #[serde(default = "default_blind")]
pub enabled_check_proof_state: bool, pub swap: AuthType,
#[serde(default = "default_blind")]
pub restore: AuthType,
#[serde(default)]
pub check_proof_state: AuthType,
} }
fn default_true() -> bool { fn default_blind() -> AuthType {
true AuthType::Blind
} }
/// CDK settings, derived from `config.toml` /// CDK settings, derived from `config.toml`
#[derive(Debug, Clone, Serialize, Deserialize, Default)] #[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Settings { pub struct Settings {

View File

@@ -4,19 +4,28 @@ use std::env;
use crate::config::Auth; use crate::config::Auth;
pub const ENV_AUTH_ENABLED: &str = "CDK_MINTD_AUTH_ENABLED";
pub const ENV_AUTH_OPENID_DISCOVERY: &str = "CDK_MINTD_AUTH_OPENID_DISCOVERY"; pub const ENV_AUTH_OPENID_DISCOVERY: &str = "CDK_MINTD_AUTH_OPENID_DISCOVERY";
pub const ENV_AUTH_OPENID_CLIENT_ID: &str = "CDK_MINTD_AUTH_OPENID_CLIENT_ID"; pub const ENV_AUTH_OPENID_CLIENT_ID: &str = "CDK_MINTD_AUTH_OPENID_CLIENT_ID";
pub const ENV_AUTH_MINT_MAX_BAT: &str = "CDK_MINTD_AUTH_MINT_MAX_BAT"; pub const ENV_AUTH_MINT_MAX_BAT: &str = "CDK_MINTD_AUTH_MINT_MAX_BAT";
pub const ENV_AUTH_ENABLED_MINT: &str = "CDK_MINTD_AUTH_ENABLED_MINT"; pub const ENV_AUTH_MINT: &str = "CDK_MINTD_AUTH_MINT";
pub const ENV_AUTH_ENABLED_MELT: &str = "CDK_MINTD_AUTH_ENABLED_MELT"; pub const ENV_AUTH_GET_MINT_QUOTE: &str = "CDK_MINTD_AUTH_GET_MINT_QUOTE";
pub const ENV_AUTH_ENABLED_SWAP: &str = "CDK_MINTD_AUTH_ENABLED_SWAP"; pub const ENV_AUTH_CHECK_MINT_QUOTE: &str = "CDK_MINTD_AUTH_CHECK_MINT_QUOTE";
pub const ENV_AUTH_ENABLED_CHECK_MINT_QUOTE: &str = "CDK_MINTD_AUTH_ENABLED_CHECK_MINT_QUOTE"; pub const ENV_AUTH_MELT: &str = "CDK_MINTD_AUTH_MELT";
pub const ENV_AUTH_ENABLED_CHECK_MELT_QUOTE: &str = "CDK_MINTD_AUTH_ENABLED_CHECK_MELT_QUOTE"; pub const ENV_AUTH_GET_MELT_QUOTE: &str = "CDK_MINTD_AUTH_GET_MELT_QUOTE";
pub const ENV_AUTH_ENABLED_RESTORE: &str = "CDK_MINTD_AUTH_ENABLED_RESTORE"; pub const ENV_AUTH_CHECK_MELT_QUOTE: &str = "CDK_MINTD_AUTH_CHECK_MELT_QUOTE";
pub const ENV_AUTH_ENABLED_CHECK_PROOF_STATE: &str = "CDK_MINTD_AUTH_ENABLED_CHECK_PROOF_STATE"; pub const ENV_AUTH_SWAP: &str = "CDK_MINTD_AUTH_SWAP";
pub const ENV_AUTH_RESTORE: &str = "CDK_MINTD_AUTH_RESTORE";
pub const ENV_AUTH_CHECK_PROOF_STATE: &str = "CDK_MINTD_AUTH_CHECK_PROOF_STATE";
impl Auth { impl Auth {
pub fn from_env(mut self) -> Self { pub fn from_env(mut self) -> Self {
if let Ok(enabled_str) = env::var(ENV_AUTH_ENABLED) {
if let Ok(enabled) = enabled_str.parse() {
self.auth_enabled = enabled;
}
}
if let Ok(discovery) = env::var(ENV_AUTH_OPENID_DISCOVERY) { if let Ok(discovery) = env::var(ENV_AUTH_OPENID_DISCOVERY) {
self.openid_discovery = discovery; self.openid_discovery = discovery;
} }
@@ -31,45 +40,57 @@ impl Auth {
} }
} }
if let Ok(enabled_mint_str) = env::var(ENV_AUTH_ENABLED_MINT) { if let Ok(mint_str) = env::var(ENV_AUTH_MINT) {
if let Ok(enabled) = enabled_mint_str.parse() { if let Ok(auth_type) = mint_str.parse() {
self.enabled_mint = enabled; self.mint = auth_type;
} }
} }
if let Ok(enabled_melt_str) = env::var(ENV_AUTH_ENABLED_MELT) { if let Ok(get_mint_quote_str) = env::var(ENV_AUTH_GET_MINT_QUOTE) {
if let Ok(enabled) = enabled_melt_str.parse() { if let Ok(auth_type) = get_mint_quote_str.parse() {
self.enabled_melt = enabled; self.get_mint_quote = auth_type;
} }
} }
if let Ok(enabled_swap_str) = env::var(ENV_AUTH_ENABLED_SWAP) { if let Ok(check_mint_quote_str) = env::var(ENV_AUTH_CHECK_MINT_QUOTE) {
if let Ok(enabled) = enabled_swap_str.parse() { if let Ok(auth_type) = check_mint_quote_str.parse() {
self.enabled_swap = enabled; self.check_mint_quote = auth_type;
} }
} }
if let Ok(enabled_check_mint_str) = env::var(ENV_AUTH_ENABLED_CHECK_MINT_QUOTE) { if let Ok(melt_str) = env::var(ENV_AUTH_MELT) {
if let Ok(enabled) = enabled_check_mint_str.parse() { if let Ok(auth_type) = melt_str.parse() {
self.enabled_check_mint_quote = enabled; self.melt = auth_type;
} }
} }
if let Ok(enabled_check_melt_str) = env::var(ENV_AUTH_ENABLED_CHECK_MELT_QUOTE) { if let Ok(get_melt_quote_str) = env::var(ENV_AUTH_GET_MELT_QUOTE) {
if let Ok(enabled) = enabled_check_melt_str.parse() { if let Ok(auth_type) = get_melt_quote_str.parse() {
self.enabled_check_melt_quote = enabled; self.get_melt_quote = auth_type;
} }
} }
if let Ok(enabled_restore_str) = env::var(ENV_AUTH_ENABLED_RESTORE) { if let Ok(check_melt_quote_str) = env::var(ENV_AUTH_CHECK_MELT_QUOTE) {
if let Ok(enabled) = enabled_restore_str.parse() { if let Ok(auth_type) = check_melt_quote_str.parse() {
self.enabled_restore = enabled; self.check_melt_quote = auth_type;
} }
} }
if let Ok(enabled_check_proof_str) = env::var(ENV_AUTH_ENABLED_CHECK_PROOF_STATE) { if let Ok(swap_str) = env::var(ENV_AUTH_SWAP) {
if let Ok(enabled) = enabled_check_proof_str.parse() { if let Ok(auth_type) = swap_str.parse() {
self.enabled_check_proof_state = enabled; self.swap = auth_type;
}
}
if let Ok(restore_str) = env::var(ENV_AUTH_RESTORE) {
if let Ok(auth_type) = restore_str.parse() {
self.restore = auth_type;
}
}
if let Ok(check_proof_state_str) = env::var(ENV_AUTH_CHECK_PROOF_STATE) {
if let Ok(auth_type) = check_proof_state_str.parse() {
self.check_proof_state = auth_type;
} }
} }

View File

@@ -63,14 +63,8 @@ impl Settings {
// Check env vars for auth config even if None // Check env vars for auth config even if None
let auth = self.auth.clone().unwrap_or_default().from_env(); let auth = self.auth.clone().unwrap_or_default().from_env();
// Only set auth if env vars are present and have non-default values // Only set auth if auth_enabled flag is true
if auth.openid_discovery != String::default() if auth.auth_enabled {
|| auth.openid_client_id != String::default()
|| auth.mint_max_bat != 0
|| auth.enabled_mint
|| auth.enabled_melt
|| auth.enabled_swap
{
self.auth = Some(auth); self.auth = Some(auth);
} else { } else {
self.auth = None; self.auth = None;

View File

@@ -43,7 +43,7 @@ use cdk_axum::cache::HttpCache;
use cdk_sqlite::mint::MintSqliteAuthDatabase; use cdk_sqlite::mint::MintSqliteAuthDatabase;
use cdk_sqlite::MintSqliteDatabase; use cdk_sqlite::MintSqliteDatabase;
use cli::CLIArgs; use cli::CLIArgs;
use config::{DatabaseEngine, LnBackend}; use config::{AuthType, DatabaseEngine, LnBackend};
use env_vars::ENV_WORK_DIR; use env_vars::ENV_WORK_DIR;
use setup::LnBackendSetup; use setup::LnBackendSetup;
use tower::ServiceBuilder; use tower::ServiceBuilder;
@@ -456,116 +456,110 @@ async fn setup_authentication(
} }
}; };
let mut protected_endpoints = HashMap::new();
let mut blind_auth_endpoints = vec![];
let mut clear_auth_endpoints = vec![];
let mut unprotected_endpoints = vec![];
let mint_blind_auth_endpoint = let mint_blind_auth_endpoint =
ProtectedEndpoint::new(Method::Post, RoutePath::MintBlindAuth); ProtectedEndpoint::new(Method::Post, RoutePath::MintBlindAuth);
let mut protected_endpoints = HashMap::new();
protected_endpoints.insert(mint_blind_auth_endpoint, AuthRequired::Clear); protected_endpoints.insert(mint_blind_auth_endpoint, AuthRequired::Clear);
let mut blind_auth_endpoints = vec![]; clear_auth_endpoints.push(mint_blind_auth_endpoint);
let mut unprotected_endpoints = vec![];
// Helper function to add endpoint based on auth type
let mut add_endpoint = |endpoint: ProtectedEndpoint, auth_type: &AuthType| {
match auth_type {
AuthType::Blind => {
protected_endpoints.insert(endpoint, AuthRequired::Blind);
blind_auth_endpoints.push(endpoint);
}
AuthType::Clear => {
protected_endpoints.insert(endpoint, AuthRequired::Clear);
clear_auth_endpoints.push(endpoint);
}
AuthType::None => {
unprotected_endpoints.push(endpoint);
}
};
};
// Get mint quote endpoint
{ {
let mint_quote_protected_endpoint = let mint_quote_protected_endpoint =
ProtectedEndpoint::new(Method::Post, RoutePath::MintQuoteBolt11); ProtectedEndpoint::new(cdk::nuts::Method::Post, RoutePath::MintQuoteBolt11);
let mint_protected_endpoint = add_endpoint(mint_quote_protected_endpoint, &auth_settings.get_mint_quote);
ProtectedEndpoint::new(Method::Post, RoutePath::MintBolt11);
if auth_settings.enabled_mint {
protected_endpoints.insert(mint_quote_protected_endpoint, AuthRequired::Blind);
protected_endpoints.insert(mint_protected_endpoint, AuthRequired::Blind);
blind_auth_endpoints.push(mint_quote_protected_endpoint);
blind_auth_endpoints.push(mint_protected_endpoint);
} else {
unprotected_endpoints.push(mint_protected_endpoint);
unprotected_endpoints.push(mint_quote_protected_endpoint);
}
}
{
let melt_quote_protected_endpoint =
ProtectedEndpoint::new(Method::Post, RoutePath::MeltQuoteBolt11);
let melt_protected_endpoint =
ProtectedEndpoint::new(Method::Post, RoutePath::MeltBolt11);
if auth_settings.enabled_melt {
protected_endpoints.insert(melt_quote_protected_endpoint, AuthRequired::Blind);
protected_endpoints.insert(melt_protected_endpoint, AuthRequired::Blind);
blind_auth_endpoints.push(melt_quote_protected_endpoint);
blind_auth_endpoints.push(melt_protected_endpoint);
} else {
unprotected_endpoints.push(melt_quote_protected_endpoint);
unprotected_endpoints.push(melt_protected_endpoint);
}
}
{
let swap_protected_endpoint = ProtectedEndpoint::new(Method::Post, RoutePath::Swap);
if auth_settings.enabled_swap {
protected_endpoints.insert(swap_protected_endpoint, AuthRequired::Blind);
blind_auth_endpoints.push(swap_protected_endpoint);
} else {
unprotected_endpoints.push(swap_protected_endpoint);
}
} }
// Check mint quote endpoint
{ {
let check_mint_protected_endpoint = let check_mint_protected_endpoint =
ProtectedEndpoint::new(Method::Get, RoutePath::MintQuoteBolt11); ProtectedEndpoint::new(Method::Get, RoutePath::MintQuoteBolt11);
add_endpoint(
if auth_settings.enabled_check_mint_quote { check_mint_protected_endpoint,
protected_endpoints.insert(check_mint_protected_endpoint, AuthRequired::Blind); &auth_settings.check_mint_quote,
blind_auth_endpoints.push(check_mint_protected_endpoint); );
} else {
unprotected_endpoints.push(check_mint_protected_endpoint);
}
} }
// Mint endpoint
{
let mint_protected_endpoint =
ProtectedEndpoint::new(cdk::nuts::Method::Post, RoutePath::MintBolt11);
add_endpoint(mint_protected_endpoint, &auth_settings.mint);
}
// Get melt quote endpoint
{
let melt_quote_protected_endpoint = ProtectedEndpoint::new(
cdk::nuts::Method::Post,
cdk::nuts::RoutePath::MeltQuoteBolt11,
);
add_endpoint(melt_quote_protected_endpoint, &auth_settings.get_melt_quote);
}
// Check melt quote endpoint
{ {
let check_melt_protected_endpoint = let check_melt_protected_endpoint =
ProtectedEndpoint::new(Method::Get, RoutePath::MeltQuoteBolt11); ProtectedEndpoint::new(Method::Get, RoutePath::MeltQuoteBolt11);
add_endpoint(
if auth_settings.enabled_check_melt_quote { check_melt_protected_endpoint,
protected_endpoints.insert(check_melt_protected_endpoint, AuthRequired::Blind); &auth_settings.check_melt_quote,
blind_auth_endpoints.push(check_melt_protected_endpoint); );
} else {
unprotected_endpoints.push(check_melt_protected_endpoint);
}
} }
// Melt endpoint
{
let melt_protected_endpoint =
ProtectedEndpoint::new(Method::Post, RoutePath::MeltBolt11);
add_endpoint(melt_protected_endpoint, &auth_settings.melt);
}
// Swap endpoint
{
let swap_protected_endpoint = ProtectedEndpoint::new(Method::Post, RoutePath::Swap);
add_endpoint(swap_protected_endpoint, &auth_settings.swap);
}
// Restore endpoint
{ {
let restore_protected_endpoint = let restore_protected_endpoint =
ProtectedEndpoint::new(Method::Post, RoutePath::Restore); ProtectedEndpoint::new(Method::Post, RoutePath::Restore);
add_endpoint(restore_protected_endpoint, &auth_settings.restore);
if auth_settings.enabled_restore {
protected_endpoints.insert(restore_protected_endpoint, AuthRequired::Blind);
blind_auth_endpoints.push(restore_protected_endpoint);
} else {
unprotected_endpoints.push(restore_protected_endpoint);
}
} }
// Check proof state endpoint
{ {
let state_protected_endpoint = let state_protected_endpoint =
ProtectedEndpoint::new(Method::Post, RoutePath::Checkstate); ProtectedEndpoint::new(Method::Post, RoutePath::Checkstate);
add_endpoint(state_protected_endpoint, &auth_settings.check_proof_state);
if auth_settings.enabled_check_proof_state {
protected_endpoints.insert(state_protected_endpoint, AuthRequired::Blind);
blind_auth_endpoints.push(state_protected_endpoint);
} else {
unprotected_endpoints.push(state_protected_endpoint);
}
} }
mint_builder = mint_builder.with_auth( mint_builder = mint_builder.with_auth(
auth_localstore.clone(), auth_localstore.clone(),
auth_settings.openid_discovery, auth_settings.openid_discovery,
auth_settings.openid_client_id, auth_settings.openid_client_id,
vec![mint_blind_auth_endpoint], clear_auth_endpoints,
); );
mint_builder = mint_builder =
mint_builder.with_blind_auth(auth_settings.mint_max_bat, blind_auth_endpoints); mint_builder.with_blind_auth(auth_settings.mint_max_bat, blind_auth_endpoints);