Make the SCRAM verifier the sole password credential; finish the account-registration relay
All checks were successful
CI / check (push) Successful in 4m1s

Credentials: the SCRAM-SHA-256 verifier is now the only password
credential. PLAIN and IDENTIFY verify the plaintext against it via
scram::verify_plain, exactly as a SCRAM login proves knowledge of it, so
an account provisioned from a verifier alone (external authority) works
over every mechanism, not only SCRAM. Removed the redundant argon2 hash
entirely: the field, the Credentials member, the AccountPasswordSet
field, hash_password/verify_password, and the argon2 crate. SASL SCRAM
already forces a PBKDF2 verifier into the store, so the hash never raised
the at-rest floor. OS RNG now comes from rand_core.

Registration: finished draft/account-registration over the ircd relay.
VERIFY confirms the emailed code, RESEND reissues it, STATUS reports
state, and REGISTER now emails the code and replies verification_required
on the relay path too, not only through NickServ.
This commit is contained in:
Jean Chevronnet 2026-07-15 15:47:41 +00:00
parent 9ed40a2e7f
commit 994e8c7347
No known key found for this signature in database
12 changed files with 221 additions and 101 deletions

View file

@ -566,8 +566,8 @@ impl Engine {
}
#[cfg(test)]
pub(crate) fn test_account_hash(&self, name: &str) -> Option<String> {
self.db.test_hash(name)
pub(crate) fn test_account_verifier(&self, name: &str) -> Option<String> {
self.db.test_verifier(name)
}
#[cfg(test)]
@ -1133,6 +1133,8 @@ fn sasl_success(agent: &str, client: &str, account: String) -> Vec<NetAction> {
// Outcome of a registration attempt, rendered to the right wire response below.
enum RegOutcome {
Ok,
/// Registered, but an emailed code must be confirmed before it's verified.
VerifyRequired,
Exists,
RateLimited,
Frozen,
@ -1147,6 +1149,7 @@ fn reg_reply(reply: &RegReply, outcome: RegOutcome, account: &str) -> Vec<NetAct
RegReply::Relay { reqid, kind } => {
let (status, code, message) = match outcome {
RegOutcome::Ok => ("success", "*", "Account registered."),
RegOutcome::VerifyRequired => ("verification_required", "VERIFICATION_REQUIRED", "Registered — check your email for a code, then VERIFY."),
RegOutcome::Exists => ("error", "ACCOUNT_EXISTS", "That account name is already registered."),
RegOutcome::RateLimited => ("error", "TEMPORARILY_UNAVAILABLE", "Too many registrations, please wait a moment."),
RegOutcome::Frozen => ("error", "TEMPORARILY_UNAVAILABLE", "Registrations are temporarily frozen by network staff."),
@ -1165,8 +1168,10 @@ fn reg_reply(reply: &RegReply, outcome: RegOutcome, account: &str) -> Vec<NetAct
RegReply::NickServ { agent, uid, nick } => {
let notice = |text: String| NetAction::Notice { from: agent.clone(), to: uid.clone(), text };
match outcome {
RegOutcome::Ok => vec![
// Registering identifies you to the nick right away (drives 900).
// Registering identifies you to the nick right away (drives 900).
// VerifyRequired still logs you in; the emailed-code notice is
// appended by complete_register.
RegOutcome::Ok | RegOutcome::VerifyRequired => vec![
NetAction::Metadata { target: uid.clone(), key: "accountname".to_string(), value: nick.clone() },
notice(format!("Your nick \x02{nick}\x02 is now registered and you're logged in. Welcome!")),
],