Add openfang auth hash-password CLI command and startup warning

Addresses review feedback:
- Add `openfang auth hash-password` subcommand so users can generate
  Argon2id hashes after upgrading (the command referenced in docs).
- Emit a tracing::warn at daemon startup when auth is enabled but the
  password_hash is not in Argon2id format, so users know why login fails.
This commit is contained in:
RamXX
2026-03-19 18:12:37 -07:00
parent 8ba84ada9d
commit cf38b49e4d
2 changed files with 43 additions and 0 deletions

View File

@@ -104,6 +104,15 @@ pub async fn build_router(
.allow_headers(tower_http::cors::Any)
};
// Warn if dashboard auth is enabled but the password hash is not Argon2id.
let ph = &state.kernel.config.auth.password_hash;
if state.kernel.config.auth.enabled && !ph.is_empty() && !ph.starts_with("$argon2") {
tracing::warn!(
"Dashboard auth password_hash is not in Argon2id format. \
Login will fail. Regenerate with: openfang auth hash-password"
);
}
// Trim whitespace so `api_key = ""` or `api_key = " "` both disable auth.
let api_key = state.kernel.config.api_key.trim().to_string();
let auth_state = crate::middleware::AuthState {

View File

@@ -237,6 +237,9 @@ enum Commands {
#[arg(long)]
json: bool,
},
/// Dashboard authentication [*].
#[command(subcommand)]
Auth(AuthCommands),
/// Security tools and audit trail [*].
#[command(subcommand)]
Security(SecurityCommands),
@@ -679,6 +682,12 @@ enum CronCommands {
},
}
#[derive(Subcommand)]
enum AuthCommands {
/// Generate an Argon2id password hash for dashboard authentication.
HashPassword,
}
#[derive(Subcommand)]
enum SecurityCommands {
/// Show security status summary.
@@ -1057,6 +1066,9 @@ fn main() {
Some(Commands::Sessions { agent, json }) => cmd_sessions(agent.as_deref(), json),
Some(Commands::Logs { lines, follow }) => cmd_logs(lines, follow),
Some(Commands::Health { json }) => cmd_health(json),
Some(Commands::Auth(sub)) => match sub {
AuthCommands::HashPassword => cmd_auth_hash_password(),
},
Some(Commands::Security(sub)) => match sub {
SecurityCommands::Status { json } => cmd_security_status(json),
SecurityCommands::Audit { limit, json } => cmd_security_audit(limit, json),
@@ -5959,6 +5971,28 @@ fn cmd_health(json: bool) {
}
}
fn cmd_auth_hash_password() {
let password = prompt_input("Enter password: ");
if password.is_empty() {
ui::error("Empty password.");
std::process::exit(1);
}
let confirm = prompt_input("Confirm password: ");
if password != confirm {
ui::error("Passwords do not match.");
std::process::exit(1);
}
let hash = openfang_api::session_auth::hash_password(&password);
println!();
ui::success("Argon2id hash generated. Add this to your config.toml:");
println!();
println!(" [auth]");
println!(" enabled = true");
println!(" password_hash = \"{}\"", hash);
println!();
ui::hint("Restart the daemon after updating config.toml");
}
fn cmd_security_status(json: bool) {
let base = require_daemon("security status");
let client = daemon_client();