The Auto-Approve Dead-End: A BBS Onboarding Bug Story

August 12, 2026

Here’s a bug that’s easy to introduce and embarrassing to discover: a new user creates an account on your BBS, the system auto-approves them, generates a temporary password, and sends it to their BBS mailbox. The user can now read that password — by logging in. Which requires the password. Which is in the mailbox. Which they can’t open.

Classic chicken-and-egg. Let me walk through how this happened, why it was hard to spot, and how we fixed it.

The Setup

Phosphor BBS has two account approval modes:

  • Manual approval — a new user fills out a request form. The request goes into a queue. The sysop reviews it, sets a password, and the user gets notified (via email, if configured). The user logs in with that password.

  • Auto-approve — the request form creates the account immediately with a generated temp password. No sysop involvement. The user is told their password and can log in right away.

The auto-approve path was designed for community servers where you want low friction. Someone connects, requests an account, and they’re in. No waiting for an admin.

The Bug

Here’s what the auto-approve code did:

  1. User fills out the Account Request form with username, display name, and email.
  2. The system creates the user account with a generated temp password.
  3. The system sends the temp password to the user’s BBS mailbox.
  4. The screen shows a message: “Your account has been approved. Check your BBS mail for your temporary password.”

Step 3 is where it falls apart. BBS mail is internal — it’s not email, it’s messages delivered within the BBS system. To read BBS mail, you need to be logged in. To log in, you need the password. The password is in the BBS mail.

The user is staring at a screen telling them to check mail they can’t access. The only way out is to disconnect, somehow know the temp password, and log in. But they don’t know the password — it was mailed to an inbox they can’t open.

Dead end.

Why It Was Hard to Spot

The manual approval path worked fine because the sysop sets the password and can communicate it out-of-band (email, private message, whatever). The auto-approve path was tested, but the tests only verified that the account was created and the password was generated. They didn’t verify that the user could actually use the password. The flow looked correct in code: create user → generate password → deliver password → user logs in. It’s the delivery step that was broken, and only in the auto-approve path.

The bug was also invisible during development because the sysop account already exists. You never go through the auto-approve flow yourself once your account is set up. It only bites new users — the people least equipped to debug it.

The Fix

The fix was straightforward: when auto-approve is enabled, log the user in directly instead of mailing the password to an unreachable inbox.

AccountRequestScreen now accepts an optional BiConsumer<String, String> — a login callback that receives the username and temp password. When the account is auto-approved, if a callback is present, the screen calls it and the LoginScreen auto-authenticates:

// Auto-approve path: create the user immediately and log them in
// directly via the loginCallback. This avoids the dead-end where
// the temp password is sent to BBS mail the user can't access yet.

if (loginCallback != null) {
    loginCallback.accept(username, tempPassword);
} else {
    // Backward compat: show the password on-screen
    showTempPassword(tempPassword);
}

The LoginScreen wires the callback like this:

var requestScreen = new AccountRequestScreen(
    session, authService, requestDao,
    mailService, settingsService,
    (username, password) -> {
        // Auto-authenticate and transition to main menu
        var user = authService.authenticate(username, password);
        if (user != null) {
            session.login(user);
            session.showMainMenu();
        }
    }
);

When no callback is provided (backward compatibility), the screen falls back to showing the temp password on-screen so the user can write it down. This handles the case where AccountRequestScreen is used outside the login flow — for example, if a future admin tool creates accounts programmatically.

The request is still recorded with an audit trail (marked approved by “system”), and the sysop is still notified. The only thing that changed is the delivery mechanism: instead of BBS mail, the user is logged in directly.

The Lesson

The chicken-and-egg pattern shows up in a lot of auth flows:

  • Password reset emails that require you to be logged in to read
  • Two-factor setup that sends the code to the channel you’re trying to secure
  • Account verification that posts to a feed you need an account to see

The fix is always the same: make sure the delivery channel is accessible before the user needs the credential. In our case, the user needed the password to access the channel that delivered the password. The fix was to skip the channel entirely and deliver the credential through the same screen the user was already on.

Auto-approve is supposed to be the low-friction path. Mailing a password to an inaccessible inbox is the opposite of low friction. Now it actually is: request an account, get logged in, done. No mailbox, no disconnect, no dead end.

#BBS #terminal #authentication #jterm-bbs #opensource