Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Password Reset System

This document describes the password reset and recovery system for Listsome. Since this is a single-user instance, the approach prioritizes simplicity while maintaining security.

Overview

The system provides two methods for password recovery:

  1. Direct CLI Reset - Immediate password change (requires SSH access)
  2. Token-Based Reset - Generate a one-time token for web-based reset

Architecture

Database Schema

CREATE TABLE password_reset_tokens (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    token_hash TEXT UNIQUE NOT NULL,     -- Argon2 hash (never store plaintext)
    account_id INTEGER NOT NULL,
    created_at TIMESTAMP,
    expires_at TIMESTAMP,                 -- 1 hour expiry
    used_at TIMESTAMP,                    -- When consumed
    ip_address TEXT,                      -- Optional audit
    used BOOLEAN DEFAULT FALSE            -- Single-use flag
);

Security Features:

  • Tokens are hashed with Argon2id before storage
  • 1-hour expiration window
  • Single-use only (marked as used after consumption)
  • Automatic cleanup of expired tokens (after 24 hours)
  • Optional IP address logging for audit trail

Token Lifecycle

  1. Generation → Store hash, display plaintext once
  2. Validation → Compare hash, check expiry/usage
  3. Consumption → Mark as used, update timestamp
  4. Cleanup → Remove old tokens via trigger

CLI Tool Usage

Direct Password Reset

Use when you have SSH access to the server:

# Reset password immediately
$ listsome-cli reset-password
WARNING: This will reset the admin password immediately.
Type 'I am sure' to continue:
I am sure
Enter new password: ********
Confirm new password: ********
Password has been reset successfully.

Security: Requires exact confirmation phrase “I am sure”

Generate Reset Token

Use when you’ve forgotten the password but can access server logs:

# Generate a one-time token
$ listsome-cli generate-reset-token
This will generate a one-time password reset token.
Type 'generate' to create a token:
generate

========================================
RESET TOKEN (save this securely):
a1b2c3d4e5f6... (64 hex characters)
========================================

Use this token via:
1. Web interface: POST /reset-password with token
2. Or check server logs for the token

Token expires: 1 hour
Token is single-use

Token Format: 32 random bytes encoded as 64 hex characters

Cleanup Tokens

Remove expired and used tokens:

$ listsome-cli cleanup-tokens
Cleanup complete: removed 5 tokens

Setup (Initial Account Creation)

Create the first admin account if none exists:

$ listsome-cli setup
No admin account found. Let's create one.
Email: admin@example.com
Password: ********
Display Name: Admin User
Instance Domain (e.g., diy.example.com): diy.example.com
Setup complete! You can now log in.

Recovery Scenarios

Scenario 1: Forgot Password, Have SSH

Solution: Direct CLI reset

ssh user@server
listsome-cli reset-password
# Enter new password

Risk Level: Low - requires server access

Scenario 2: Forgot Password, No SSH

Solution: Generate token via logs

# On server (or in systemd logs)
journalctl -u listsome | grep "RESET TOKEN"
# Or check application logs
tail -f /var/log/listsome/app.log

Then use the token via web interface or cURL:

curl -X POST http://localhost:3000/reset-password \
  -H "Content-Type: application/json" \
  -d '{"token": "a1b2c3d4...", "new_password": "newpass123"}'

Risk Level: Medium - token exposure in logs

Scenario 3: Complete Lockout

Solution: Physical server access required

If you have:

  • No SSH access
  • No web access
  • Forgot password

You must have physical access to the server to:

  1. Access console directly
  2. Mount the SQLite database
  3. Delete the account row to trigger re-setup
  4. Or use SQL to manually update password_hash
# Emergency: Delete account to re-trigger setup
sqlite3 data.db "DELETE FROM account WHERE id = 1;"
# Then run setup again
listsome-cli setup

Risk Level: High - requires physical security breach

Security Considerations

Threat Model

Assumed attackers:

  • Someone with web access (can try tokens)
  • Someone with file system read access (can see tokens in logs)
  • Someone with database access (can see token hashes)

Mitigations:

  • Tokens expire in 1 hour (reduces window of attack)
  • Tokens are single-use (prevents replay)
  • Tokens are hashed (attacker with DB access can’t use tokens)
  • Direct reset requires confirmation phrase (prevents accidental execution)

Audit Trail

All CLI operations log to stderr:

[CLI] Starting password reset process
[CLI] Connecting to database: sqlite://data.db
[CLI] Password updated successfully for account_id=1

Recommendation: Redirect stderr to syslog:

listsome-cli reset-password 2> >(logger -t listsome-cli)

File Permissions

The CLI should be protected by file permissions:

# Make CLI only executable by owner
chmod 750 listsome-cli
chown root:admin listsome-cli

# Database should be readable only by application user
chmod 640 data.db
chown listsome:listsome data.db

Implementation Details

Token Generation

Uses cryptographically secure random bytes:

#![allow(unused)]
fn main() {
let token_bytes: Vec<u8> = (0..32).map(|_| rand::random::<u8>()).collect();
let token = hex::encode(&token_bytes);  // 64 hex chars
}

Entropy: 256 bits (comparable to JWT signing keys)

Hash Algorithm

Uses Argon2id (same as password hashing):

  • Memory-hard (resists GPU cracking)
  • Salted (each token produces different hash)
  • Tunable parameters

Database Cleanup

Automatic trigger runs on INSERT:

DELETE FROM password_reset_tokens 
WHERE expires_at < datetime('now', '-24 hours')
   OR (used = TRUE AND used_at < datetime('now', '-7 days'));

Future Enhancements

Possible improvements (not currently needed for single-user):

  1. Email Integration - Send tokens via email using lettre
  2. Rate Limiting - Limit token generation per IP
  3. Hardware Token - Require Yubikey for CLI operations
  4. Recovery Keys - Generate offline backup codes during setup
  5. Web UI - Add /forgot-password page for self-service

Testing

Run tests for the password reset system:

# Run all auth tests
cargo test auth

# Run specific token tests
cargo test test_create_reset_token
cargo test test_validate_and_consume_token

Troubleshooting

Token Not Working

Checklist:

  • Token hasn’t expired (1 hour limit)
  • Token hasn’t been used already
  • Token is complete (64 characters)
  • Database is accessible

CLI Can’t Connect to Database

# Check DATABASE_URL environment variable
echo $DATABASE_URL

# Default is sqlite://data.db
# Override: export DATABASE_URL="sqlite:///path/to/data.db"

Database Locked

If SQLite is locked by the running web server:

# Stop the web server temporarily
systemctl stop listsome
listsome-cli reset-password
systemctl start listsome

References