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.
  • CSS 46.3%
  • Python 34.7%
  • HTML 18.6%
  • JavaScript 0.4%
Find a file
2026-10-12 13:12:06 -05:00
brazen feat: send emails through Django's mail backend (console by default) 2026-10-12 13:09:24 -05:00
core test: add regression tests for permissions, tokens, duplicates and password handling 2026-10-12 13:09:24 -05:00
docs docs: add login page screenshot 2026-10-12 13:12:06 -05:00
src add x-data to icons, start email/phone validation 2025-03-04 09:28:36 -06:00
static fix minor formatting bug' 2025-07-26 21:24:47 -05:00
templates fix(security): enforce permissions on the backend for user management 2026-10-12 13:09:24 -05:00
.env.example docs: add screenshot, clone step and uv setup instructions to the README 2026-10-12 13:12:06 -05:00
.gitignore Initial commit 2025-02-18 02:04:27 +00:00
.python-version build: switch to uv 2026-10-12 13:12:06 -05:00
LICENSE docs: write a real README and use the full name in the license 2026-10-12 13:09:24 -05:00
manage.py initial commit 2025-02-17 21:20:06 -06:00
package-lock.json cotton + tailwind + dasiyui setup 2025-02-17 23:37:35 -06:00
package.json cotton + tailwind + dasiyui setup 2025-02-17 23:37:35 -06:00
pyproject.toml build: switch to uv 2026-10-12 13:12:06 -05:00
README.md docs: add screenshot, clone step and uv setup instructions to the README 2026-10-12 13:12:06 -05:00
uv.lock build: switch to uv 2026-10-12 13:12:06 -05:00

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.

The brazen login page

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, and EmailVerify, which stores each emailed link
  • core/views.py: login, invite, verify, reset and user-management views
  • core/decorators.py and core/permissions.py: auth_required, staff_required, superuser_required and the can_manage() rule that decides who may change whom
  • core/tasks.py: builds and sends the emails, and tracks whether each send succeeded
  • core/validation.py: input validation that returns a value or an error message
  • brazen/middleware/logrequest.py: request logging
  • templates/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.