Skip to content

AdeshDeshmukh/warden-auth-cli

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

19 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ” Warden Auth CLI

  โ–ˆโ–ˆโ•—    โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ•—   โ–ˆโ–ˆโ•—
  โ–ˆโ–ˆโ•‘    โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•โ–ˆโ–ˆโ–ˆโ–ˆโ•—  โ–ˆโ–ˆโ•‘
  โ–ˆโ–ˆโ•‘ โ–ˆโ•— โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘  โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—  โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘
  โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘  โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ•  โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘
  โ•šโ–ˆโ–ˆโ–ˆโ•”โ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘  โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘  โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘ โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ•‘
   โ•šโ•โ•โ•โ•šโ•โ•โ• โ•šโ•โ•  โ•šโ•โ•โ•šโ•โ•  โ•šโ•โ•โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ•โ•โ•โ•โ•โ•โ•šโ•โ•  โ•šโ•โ•โ•โ•

Production-grade containerized CLI authentication system

Go Docker SQLite License Security


๐Ÿ“Œ Overview

Warden Auth CLI is a secure, containerized command-line authentication system built entirely in Go. It implements user registration, password-based authentication, optional TOTP two-factor authentication, and persistent session management โ€” all running inside Docker with SQLite for storage.

This project was built to demonstrate production-grade backend engineering practices including clean architecture, layered separation of concerns, security-first design, and professional DevOps packaging.


โœจ Key Highlights

Feature Implementation
Password Hashing Argon2id โ€” OWASP 2023 recommended, memory-hard algorithm
Session Security SHA256-hashed tokens โ€” raw token never stored on disk
Account Protection Progressive lockout โ€” escalating delays before hard lock
Two-Factor Auth TOTP โ€” Google Authenticator and Authy compatible
Audit Trail Structured JSON logs โ€” every security event recorded
Container Security Distroless image โ€” zero shell, zero OS attack surface
Architecture 5-layer clean architecture โ€” fully decoupled and testable
Data Persistence SQLite with WAL mode โ€” survives container restarts

๐Ÿ—๏ธ Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                        Docker Container                         โ”‚
โ”‚                   gcr.io/distroless/static                      โ”‚
โ”‚                                                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚                    Warden Auth CLI                       โ”‚   โ”‚
โ”‚  โ”‚                                                          โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚   โ”‚
โ”‚  โ”‚  โ”‚    CLI    โ”‚โ”€โ”€โ–ถโ”‚  Service  โ”‚โ”€โ”€โ–ถโ”‚   Repository     โ”‚    โ”‚   โ”‚
โ”‚  โ”‚  โ”‚   Layer   โ”‚   โ”‚   Layer   โ”‚   โ”‚     Layer        โ”‚    โ”‚   โ”‚
โ”‚  โ”‚  โ”‚           โ”‚   โ”‚           โ”‚   โ”‚                  โ”‚    โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ readline  โ”‚   โ”‚  Argon2id โ”‚   โ”‚  SQLite + WAL    โ”‚    โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ pterm     โ”‚   โ”‚  TOTP     โ”‚   โ”‚  Migrations      โ”‚    โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ display   โ”‚   โ”‚  Sessions โ”‚   โ”‚  Audit Logs      โ”‚    โ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚   โ”‚
โ”‚  โ”‚         โ”‚               โ”‚                  โ”‚             โ”‚   โ”‚
โ”‚  โ”‚         โ–ผ               โ–ผ                  โ–ผ             โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚   โ”‚
โ”‚  โ”‚  โ”‚                  Domain Layer                    โ”‚    โ”‚   โ”‚
โ”‚  โ”‚  โ”‚     Models โ€ข Interfaces โ€ข Sentinel Errors        โ”‚    โ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                              โ”‚                                  โ”‚
โ”‚              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                  โ”‚
โ”‚              โ–ผ                               โ–ผ                  โ”‚
โ”‚     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚     โ”‚   db_data vol   โ”‚           โ”‚  log_data vol   โ”‚           โ”‚
โ”‚     โ”‚   warden.db     โ”‚           โ”‚  warden.log     โ”‚           โ”‚
โ”‚     โ”‚   (SQLite)      โ”‚           โ”‚  (JSON audit)   โ”‚           โ”‚
โ”‚     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Layer Responsibilities

Layer Package Rule
Domain internal/domain Zero external imports. Defines models, interfaces, errors.
Repository internal/repository/sqlite All SQL lives here. Maps DB errors to domain errors.
Service internal/service Business logic only. No SQL, no CLI, no display code.
CLI internal/cli Input, routing, display only. No business logic.
Config internal/config ENV loading with defaults. Single Config struct.
Logger internal/logger Dual slog output โ€” JSON to file, text to stdout.

๐Ÿš€ Quick Start

Requirements: Docker and Docker Compose installed.

git clone https://github.com/AdeshDeshmukh/warden-auth-cli.git
cd warden-auth-cli
cp .env.example .env
docker-compose run --rm warden

That is it. The application starts, migrations run automatically, and you are at the prompt.


๐Ÿ“– Commands Reference

Before Login

Command Description
register Create a new user account with password validation
login Authenticate โ€” prompts for TOTP code if 2FA is enabled
help Display all available commands
exit Quit the application cleanly

After Login

Command Description
whoami Display full profile โ€” username, registration date, 2FA status, session expiry, last login
enable-2fa Generate TOTP secret, display setup key, verify before saving
disable-2fa Verify current TOTP code then remove 2FA from account
logout Invalidate session token and return to login prompt
help Display all available commands

๐Ÿ–ฅ๏ธ Usage Examples

Register

warden โฏ register
  Username: adesh
  Password: โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข
  Confirm Password: โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข
โœ… Account created successfully. You can now login.

Login

warden โฏ login
  Username: adesh
  Password: โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข
โœ… Welcome back, adesh!

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Field           โ”‚ Value                      โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Username        โ”‚ adesh                      โ”‚
โ”‚ Registered      โ”‚ 2025-06-18                 โ”‚
โ”‚ Last Login      โ”‚ Just now                   โ”‚
โ”‚ 2FA Status      โ”‚ Disabled                   โ”‚
โ”‚ Session Expires โ”‚ in 30m 0s                  โ”‚
โ”‚ Account Status  โ”‚ Active                     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Login with 2FA Enabled

warden โฏ login
  Username: adesh
  Password: โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข
  2FA Code: โ€ขโ€ขโ€ขโ€ขโ€ขโ€ข
โœ… Welcome back, adesh!

Enable Two-Factor Authentication

warden [adesh] โฏ enable-2fa
โ„น๏ธ  Scan the QR code below with Google Authenticator or Authy:
โ„น๏ธ  otpauth URL: otpauth://totp/Warden%20Auth:adesh?...
โ„น๏ธ  Manual entry key: JBSWY3DPEHPK3PXP
  Enter the 6-digit code from your authenticator app to confirm: โ€ขโ€ขโ€ขโ€ขโ€ขโ€ข
โœ… 2FA enabled successfully. Your account is now protected.

Failed Login with Progressive Lockout

warden โฏ login
  Username: adesh
  Password: โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข
โŒ Invalid credentials. Please try again.

warden โฏ login
  Username: adesh
  Password: โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข
โŒ Invalid credentials. Please try again.

[after 5 failed attempts]

โŒ Too many failed attempts.
โš ๏ธ  Account locked. Try again in 15m 0s.

โš™๏ธ Configuration

Copy .env.example to .env and modify as needed. All values below are defaults.

Variable Default Description
DB_PATH ./data/warden.db SQLite database file location
LOG_PATH ./logs/warden.log JSON audit log file location
SESSION_TIMEOUT 30m Session duration before automatic expiry
MAX_FAILED_ATTEMPTS 5 Failed login threshold before account lockout
LOCKOUT_DURATION 15m Duration of account lockout after threshold reached
ARGON_MEMORY 65536 Argon2id memory parameter in KB (64MB)
ARGON_ITERATIONS 3 Argon2id iteration count
ARGON_PARALLELISM 2 Argon2id parallel thread count

๐Ÿ”’ Security Design Decisions

Argon2id over bcrypt

bcrypt is CPU-hard only. Argon2id is both CPU-hard and memory-hard. The memory requirement (64MB per attempt) defeats GPU-based brute force attacks because GPU cores share limited memory bandwidth โ€” thousands of parallel attempts become impossible. OWASP has recommended Argon2id as the preferred password hashing algorithm since 2023. The parameters used here meet OWASP minimum requirements.

SHA256 session token hashing

Storing raw session tokens in a database is equivalent to storing plaintext passwords. If the database file is ever compromised, an attacker would immediately control all active sessions. By storing SHA256(rawToken) instead, the raw token exists only in the running process memory and is never written to persistent storage. SHA256 is a one-way function โ€” the stored hash cannot be reversed.

Progressive lockout over hard cutoff

A hard cutoff at N attempts is easy to detect and probe during an attack. Progressive delays (2 second artificial sleep from the third failed attempt onward) make brute force attempts slow and painful before the hard lockout triggers at 5 attempts. The exact countdown timer shown on lockout informs legitimate users without leaking timing internals to attackers.

Single session per user

When a new login succeeds, all existing sessions for that user are deleted before the new session is created. This prevents session fixation attacks where an attacker plants a known session identifier and waits for the victim to authenticate with it. It also prevents stale sessions from accumulating indefinitely.

Distroless final Docker image

Standard base images (ubuntu, alpine) contain shells, package managers, and hundreds of OS-level binaries. Each is a potential attack vector. The gcr.io/distroless/static-debian12 image contains only the compiled binary and minimal runtime. There is no /bin/sh โ€” shell injection is structurally impossible. There is no package manager โ€” supply chain attacks via OS packages cannot occur.

Pure Go SQLite driver

Using modernc.org/sqlite instead of the CGo-based mattn/go-sqlite3 means CGO_ENABLED=0 during build. This produces a fully static binary with zero shared library dependencies, which is what makes the distroless final image possible. A CGo build would require glibc in the final image, forcing a larger and less secure base.

Timing attack prevention

When a username does not exist, the login handler still runs the full Argon2id computation on a dummy value before returning an error. Without this, an attacker could enumerate valid usernames by measuring response time โ€” a non-existent user would return faster than an existing one requiring hash verification.

TOTP code verified before secret is saved

When enabling 2FA, the user must successfully enter a valid six-digit code before the secret is written to the database. This proves the user has correctly scanned the setup key and their authenticator app is generating valid codes. Without this verification step, a user could enable 2FA with a misconfigured app and then be permanently unable to log in.

Audit logs as a first-class feature

Every authentication event writes simultaneously to the audit_logs SQLite table and to a structured JSON log file. The JSON format is directly ingestible by SIEM tools such as Splunk, Elastic Stack, and Datadog. The username field is denormalized intentionally โ€” the audit trail remains intact even if a user account is deleted.


๐Ÿ—„๏ธ Database Schema

users

Column Type Description
id TEXT UUID primary key โ€” prevents enumeration attacks
username TEXT Unique, 3-32 characters, alphanumeric and underscore
password_hash TEXT Argon2id encoded hash โ€” self-contained with salt and params
totp_secret TEXT TOTP secret key, null when 2FA is disabled
totp_enabled INTEGER 1 when 2FA is active, 0 otherwise
failed_attempts INTEGER Failed login counter โ€” persists across container restarts
locked_until DATETIME Lockout expiry timestamp, null when account is active
last_login_at DATETIME Timestamp of most recent successful authentication
created_at DATETIME Account creation timestamp in UTC

sessions

Column Type Description
id TEXT UUID primary key
user_id TEXT Foreign key to users.id โ€” cascades on delete
token_hash TEXT SHA256 of raw session token โ€” raw token never stored
expires_at DATETIME Session expiry โ€” validated on every command
created_at DATETIME Session creation timestamp

audit_logs

Column Type Description
id INTEGER Auto-increment primary key
user_id TEXT Foreign key to users.id โ€” set null on delete
username TEXT Denormalized โ€” audit trail survives account deletion
event TEXT Event type constant from predefined set
detail TEXT JSON metadata string with event-specific context
created_at DATETIME Event timestamp in UTC

๐Ÿ“Š Audit Events Reference

Event Triggered When Detail Fields
USER_REGISTERED New account created {}
LOGIN_SUCCESS Password verified, session created {}
LOGIN_FAILED Wrong password or locked account reason, attempts_remaining
ACCOUNT_LOCKED Failed attempts hit maximum locked_until
TOTP_ENABLED 2FA setup completed and verified {}
TOTP_DISABLED 2FA removed from account {}
SESSION_CREATED New session token issued expires_at
SESSION_EXPIRED Expired session detected on validation {}
LOGOUT User explicitly ended session {}

Sample JSON Audit Log Entry

{
  "time": "2025-06-18T10:23:01.234Z",
  "level": "INFO",
  "msg": "security_event",
  "event": "LOGIN_SUCCESS",
  "username": "adesh",
  "detail": {}
}

๐Ÿณ Docker Details

Multi-Stage Build

Stage 1 โ€” builder uses golang:1.25-alpine:

  • Downloads all Go module dependencies
  • Compiles with CGO_ENABLED=0 for a fully static binary
  • Strips debug info with -ldflags="-w -s" for smaller size

Stage 2 โ€” final uses gcr.io/distroless/static-debian12:

  • Contains only the compiled binary
  • No shell, no package manager, no OS utilities
  • Minimal CVE exposure from base image

Image Size Comparison

Base Image Typical Size
ubuntu ~180MB
alpine ~20MB
distroless/static ~15MB

Persistent Volumes

Volume Mount Point Contents
db_data /app/data SQLite database file
log_data /app/logs Structured JSON audit log files

Both volumes survive container restarts, rebuilds, and updates. Your data is never lost when stopping or updating the container.


๐Ÿงช Tests

go test -v -race ./tests/...
Test What It Validates
TestPasswordStrength_TooShort Rejects passwords under 8 characters
TestPasswordStrength_NoUppercase Rejects passwords without uppercase
TestPasswordStrength_NoLowercase Rejects passwords without lowercase
TestPasswordStrength_NoDigit Rejects passwords without a digit
TestPasswordStrength_CommonPassword Rejects passwords on common blocklist
TestPasswordStrength_ValidPassword Accepts a strong valid password
TestTOTPGenerate_ReturnsSecretAndURL TOTP generation returns non-empty values
TestTOTPGenerate_DifferentSecretsEachTime Each call generates a unique secret
TestTOTPVerify_InvalidCode Rejects wrong 6-digit codes
TestTOTPVerify_EmptyCode Rejects empty code input

๐Ÿ“ Project Structure

warden-auth-cli/
โ”œโ”€โ”€ cmd/
โ”‚   โ””โ”€โ”€ main.go                     Entry point โ€” config, DB, wiring, shutdown
โ”œโ”€โ”€ internal/
โ”‚   โ”œโ”€โ”€ config/
โ”‚   โ”‚   โ””โ”€โ”€ config.go               ENV variable loading with typed defaults
โ”‚   โ”œโ”€โ”€ domain/
โ”‚   โ”‚   โ”œโ”€โ”€ user.go                 User model with IsLocked, LockoutRemaining
โ”‚   โ”‚   โ”œโ”€โ”€ session.go              Session model with IsExpired, TimeRemaining
โ”‚   โ”‚   โ”œโ”€โ”€ audit.go                AuditEvent constants and AuditLog struct
โ”‚   โ”‚   โ”œโ”€โ”€ errors.go               Sentinel errors for all domain failures
โ”‚   โ”‚   โ””โ”€โ”€ repository.go           Repository interfaces owned by domain layer
โ”‚   โ”œโ”€โ”€ migrations/
โ”‚   โ”‚   โ”œโ”€โ”€ migrations.go           go:embed entry point for SQL files
โ”‚   โ”‚   โ””โ”€โ”€ sql/
โ”‚   โ”‚       โ”œโ”€โ”€ 001_users.sql       Users table schema
โ”‚   โ”‚       โ”œโ”€โ”€ 002_sessions.sql    Sessions table with token_hash indexes
โ”‚   โ”‚       โ””โ”€โ”€ 003_audit_logs.sql  Audit log table with event indexes
โ”‚   โ”œโ”€โ”€ repository/
โ”‚   โ”‚   โ””โ”€โ”€ sqlite/
โ”‚   โ”‚       โ”œโ”€โ”€ db.go               Connection, WAL mode, migration runner
โ”‚   โ”‚       โ”œโ”€โ”€ user_repo.go        UserRepository SQLite implementation
โ”‚   โ”‚       โ”œโ”€โ”€ session_repo.go     SessionRepository SQLite implementation
โ”‚   โ”‚       โ””โ”€โ”€ audit_repo.go       AuditRepository โ€” fire and forget
โ”‚   โ”œโ”€โ”€ service/
โ”‚   โ”‚   โ”œโ”€โ”€ auth.go                 Register, Login, lockout, password strength
โ”‚   โ”‚   โ”œโ”€โ”€ session.go              Create, Validate, Invalidate sessions
โ”‚   โ”‚   โ””โ”€โ”€ totp.go                 Generate, Enable, Disable, Verify TOTP
โ”‚   โ”œโ”€โ”€ cli/
โ”‚   โ”‚   โ”œโ”€โ”€ app.go                  readline loop, state machine, routing
โ”‚   โ”‚   โ”œโ”€โ”€ pre_login.go            register and login command handlers
โ”‚   โ”‚   โ”œโ”€โ”€ post_login.go           whoami, 2FA, logout command handlers
โ”‚   โ”‚   โ””โ”€โ”€ display.go              All terminal output โ€” tables, colors, formatting
โ”‚   โ””โ”€โ”€ logger/
โ”‚       โ””โ”€โ”€ logger.go               Dual slog handler โ€” JSON file and text stdout
โ”œโ”€โ”€ tests/
โ”‚   โ”œโ”€โ”€ auth_test.go                Password strength and hashing unit tests
โ”‚   โ””โ”€โ”€ totp_test.go                TOTP generation and verification unit tests
โ”œโ”€โ”€ migrations/                     Original SQL files (also embedded via internal/migrations)
โ”œโ”€โ”€ .env.example                    All environment variables documented with defaults
โ”œโ”€โ”€ .dockerignore                   Excludes secrets and build artifacts from image
โ”œโ”€โ”€ .gitignore                      Excludes .env, binaries, runtime data from git
โ”œโ”€โ”€ Dockerfile                      Multi-stage distroless build
โ”œโ”€โ”€ docker-compose.yml              Service definition with persistent named volumes
โ”œโ”€โ”€ Makefile                        Developer workflow targets
โ”œโ”€โ”€ LICENSE                         MIT License
โ””โ”€โ”€ README.md                       This document

๐Ÿ”ง Local Development

# Run locally without Docker
make run

# Build binary
make build
./bin/warden

# Run all tests with race detector
make test

# Generate HTML coverage report
make test-cover

# Run linter
make lint

# Clean build artifacts
make clean

Available Make Targets

Target Description
make build Compile binary to bin/warden
make run Run locally with go run
make test Run all tests with race detector
make test-cover Generate HTML test coverage report
make docker-up Build and start with docker-compose
make docker-down Stop containers and remove volumes
make lint Run go vet across all packages
make clean Remove build artifacts

โœ… Requirements Coverage

Requirement Status Implementation
User registration โœ… register command with username and password validation
Login with password โœ… login command with Argon2id verification
Optional TOTP 2FA โœ… enable-2fa and disable-2fa with Google Authenticator
Secure password storage โœ… Argon2id with random salt, OWASP recommended
Account lockout โœ… Progressive delays then 15-minute hard lockout
Session management โœ… Configurable timeout, DB-backed, validated per command
SQLite persistence โœ… WAL mode, named Docker volume, survives restarts
Interactive prompt โœ… readline with command history and tab completion
Clear error messages โœ… Colored pterm output with descriptive messages
Help command โœ… Context-aware โ€” different commands before and after login
whoami command โœ… Full profile table auto-displayed on login
Docker container โœ… Multi-stage distroless build
README documentation โœ… This document
Database schema โœ… Three migration files, run automatically on startup
Unit tests โœ… 10 tests covering password strength and TOTP

๐Ÿ”ฎ What I Would Add With More Time

  • WebAuthn / FIDO2 hardware security key support as a second authentication factor
  • IP-based rate limiting to complement per-user account lockout for distributed attacks
  • Multi-device session management with the ability to view and revoke individual sessions
  • TOTP backup codes generated at 2FA setup for emergency account recovery
  • Admin CLI commands to view audit logs, unlock accounts, and list active sessions
  • Automated integration tests using an in-memory SQLite database for full flow coverage
  • Session refresh to extend expiry on active use without requiring re-login

๐Ÿ‘จโ€๐Ÿ’ป Author

Adesh Deshmukh

GitHub LinkedIn


๐Ÿ“„ License

MIT License โ€” Copyright ยฉ 2025 Adesh Deshmukh

See LICENSE for full terms.

About

Security-hardened Go CLI authentication system with Argon2id, TOTP 2FA, Clean Architecture, audit logging, and distroless Docker

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors