- CSS 46.3%
- Python 34.7%
- HTML 18.6%
- JavaScript 0.4%
| brazen | ||
| core | ||
| docs | ||
| src | ||
| static | ||
| templates | ||
| .env.example | ||
| .gitignore | ||
| .python-version | ||
| LICENSE | ||
| manage.py | ||
| package-lock.json | ||
| package.json | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
brazen
A user-management and authentication portal built with Django. It covers the account lifecycle that most internal tools need: invite a user by email, let them set their own password, verify addresses, reset forgotten passwords, and give managers and admins the right level of control over other accounts.
The UI is server-rendered with Tailwind, DaisyUI and Alpine AJAX, so pages update in place without a JavaScript framework.
Features
- Roles: regular users, managers and admins, enforced on the backend (not just hidden in the UI)
- Invite flow: a manager adds a user, the user gets an activation link and sets their own password
- Email verification and password reset: single-use links that expire, bound to the purpose they were issued for
- User management page: edit name, username, email and phone, activate or deactivate accounts, send verification or reset emails, promote and revoke managers (admins only)
- Input validation: usernames, names, emails, phones and password strength, with clear error messages
- Notifications: users are told when their privileges change
- Request logging: middleware logs the method, URL, status and timing of every request
- Console email in development: links print to the terminal, and switching to SMTP is a matter of environment variables
Roles
| Role | Can do |
|---|---|
| User | Log in, reset their own password |
| Manager | Everything a user can, plus add users and manage regular users and themselves |
| Admin | Everything a manager can, plus manage other managers and promote or revoke manager status |
Managers cannot edit, deactivate or reset other managers or admins, and nobody can deactivate their own account.
Tech stack
- Python 3.10+ and Django 5.1
- SQLite (in WAL mode)
- django-cotton for template components
- Tailwind CSS 4 and DaisyUI 5 with a custom theme
- Alpine.js with the Alpine AJAX plugin, and anime.js for the entrance animations
Getting started
You need uv (it installs Python and the dependencies for you). The app supports Python 3.10 or newer.
# 0. Get the code
git clone https://git.turngear.dev/josh/brazen.git
cd brazen
# 1. Install the dependencies into a local virtual environment
uv sync
# 2. Configure your environment
cp .env.example .env
# open .env and set DJANGO_SECRET_KEY (the file shows how to generate one)
# 3. Create the database and an admin account
uv run python manage.py migrate
uv run python manage.py createsuperuser
# 4. Run it
uv run python manage.py runserver
Open http://localhost:8000 and log in with the account you just created.
To see the invite flow: go to Users, click Add User, and watch the terminal. The activation email (with its link) is printed there. Open the link to set the new user's password.
The compiled CSS is committed, so you only need Node if you change the styles:
npm install
npm run dev # rebuilds static/css/style.css as you edit src/input.css
Configuration
Settings come from environment variables or a .env file. See .env.example for the full list.
| Variable | Purpose | Default |
|---|---|---|
DJANGO_SECRET_KEY |
Django's signing key. Required unless DJANGO_DEBUG is on |
none |
DJANGO_DEBUG |
Enables debug mode. Use only for local development | False |
DJANGO_ALLOWED_HOSTS |
Comma-separated hostnames the site may be served from | localhost,127.0.0.1 |
BASE_URL |
Base URL used to build links in emails | http://localhost:8000 |
EMAIL_BACKEND |
Console backend for development, SMTP backend for real delivery | console |
SMTP_SERVER, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD |
SMTP connection details | none |
SMTP_USE_SSL, SMTP_USE_TLS |
Connection security | SSL on, TLS off |
DEFAULT_FROM_EMAIL, SUPPORT_EMAIL |
Sender address and the support address shown in emails | noreply@localhost, support@example.com |
LOG_FILE |
Where the application log is written | debug.log |
Running the tests
uv run python manage.py test
The suite covers the permission rules, the invite and reset flows, token handling, duplicate detection, validation, and checks that passwords never reach the output or the logs.
How it works
core/models.py: a custom user model (CoreUser), notifications, andEmailVerify, which stores each emailed linkcore/views.py: login, invite, verify, reset and user-management viewscore/decorators.pyandcore/permissions.py:auth_required,staff_required,superuser_requiredand thecan_manage()rule that decides who may change whomcore/tasks.py: builds and sends the emails, and tracks whether each send succeededcore/validation.py: input validation that returns a value or an error messagebrazen/middleware/logrequest.py: request loggingtemplates/cotton/: reusable template components and layouts
An emailed link carries a random token (uuid4). Opening it marks the record used, then sends the user to the matching page (verify, reset or create password). The token only works on the page for its own type, expires, and cannot be reused.
Roadmap
- Move to Django 5.2 LTS (needs django-cotton 2.x and a template update)
- Self-service "My Settings" page and profile photos
- App-wide settings (the models for per-instance SMTP and branding exist but are not wired up)
- Rate limiting on login and password reset requests
- Secure-cookie and HTTPS settings for production deployments
License
MIT. See LICENSE.
