DEV Community

Cover image for I got tired of rebuilding Flask auth from scratch — so I built a proper starter kit
Gaurav Digari
Gaurav Digari

Posted on

I got tired of rebuilding Flask auth from scratch — so I built a proper starter kit

Every Flask project I started followed the same painful pattern.

Day 1: excited about the idea.
Day 2: still setting up signup and login.
Day 3: debugging OTP email delivery.
Day 4: building an admin panel to manage users.
Day 5: finally writing the first line of the actual idea.

Sound familiar?

After the third time doing this, I stopped and built it properly once — a production-ready Flask authentication and admin foundation I could drop into any new project.

What I actually built

Not a tutorial-level "here's how Flask-Login works" setup. A real, production-ready codebase with actual security practices:

Authentication

  • Signup and login with secure session handling
  • Email OTP verification — 6-digit code, expires in 10 minutes, resend with cooldown
  • Password complexity enforced both client-side and server-side (not just a frontend hint)
  • Forgot/reset password with single-use, expiring tokens
  • Passwords hashed with bcrypt — never stored plain

Security

  • Rate limiting on login, OTP requests, and password reset (Flask-Limiter)
  • CSRF protection on every form
  • Security headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy)
  • Cache-control headers that prevent browsers from showing stale authenticated pages on back/forward navigation
  • Input validation and sanitization on every endpoint
  • No secrets hardcoded anywhere — everything from environment variables

Admin panel

  • Dashboard with total users, active users, new signups this week
  • Searchable, paginated user table
  • Enable/disable, promote to admin, or delete users
  • Admins land directly on the admin panel after login — no unnecessary redirect through the user dashboard
  • Cascade-safe user deletion (no orphaned OTP records crashing the delete)

Code quality

  • Flask application factory pattern with Blueprints (auth, admin, main)
  • SQLAlchemy ORM with Flask-Migrate for database migrations
  • Environment-based config (development/production/testing)
  • pytest test suite covering signup, OTP verification, login, wrong password, unverified account blocking
  • Background email sending — SMTP happens on a thread so the page responds instantly instead of waiting

UI

  • Tailwind CSS via CDN — zero build step required
  • Custom fonts (Outfit + Inter + JetBrains Mono)
  • Responsive split-panel auth layout
  • Show/hide password toggle on every password field
  • Flash messages with close button and 5-second auto-dismiss
  • Custom error pages for 403, 404, 429, 500 — no raw stack traces ever shown

Things I learned building this

Background threads for email sending matter more than you think. My first version blocked the request while the SMTP connection happened. A misconfigured hostname would hang the entire request for 30+ seconds with no feedback to the user. Moving email dispatch to a daemon thread made every auth action feel instant.

The browser cache is a security issue. Without explicit Cache-Control: no-store headers, pressing back after logout can show a stale authenticated page directly from browser memory — the server never even gets asked. Most Flask tutorials don't cover this.

Cascade deletes need explicit configuration. SQLAlchemy's default behavior when you delete a User with related OTPCode rows is to try setting user_id = NULL on those rows — which fails immediately if user_id is NOT NULL (as it should be). You need cascade="all, delete-orphan" on the relationship, not just the foreign key.

CSP blocks your own inline scripts. I added a Content-Security-Policy header that blocked the inline Tailwind config script defining my custom color tokens. Every button existed and worked but rendered invisible — white text on white background. Spent longer than I'd like to admit on that one.

The stack

  • Python 3.10+, Flask 3.0
  • SQLAlchemy + Flask-Migrate
  • Flask-Login, Flask-WTF, Flask-Mail, Flask-Limiter
  • Tailwind CSS (CDN)
  • SQLite for development, Postgres-ready for production
  • pytest

Where to get it

If you're building a Flask project and don't want to spend days on auth infrastructure, it's available here:

👉 Flask SaaS Starter Kit on Gumroad

Comes with a README, a step-by-step SETUP guide written for people who aren't Flask experts yet, and a CUSTOMIZE guide for extending the user model and swapping email providers.

Happy to go deep on any of the implementation decisions — the rate limiting strategy, the OTP flow design, session security, the background email threading. Drop a comment.

What does your auth setup usually look like for new Flask projects?

Top comments (1)

Collapse
 
vsdudakov profile image
Seva D

The day-by-day breakdown is painfully accurate — auth and the admin panel are exactly the two things that eat the first week of every side project.

One thought on the admin half of that: it can be a dependency instead of code you maintain inside the kit. FastAdmin (github.com/vsdudakov/fastadmin — disclosure: I maintain it) mounts as a Flask blueprint on top of your SQLAlchemy models and gives you the Django-Admin-style CRUD, filters, search, and user management out of the box, so the kit's admin wouldn't need hand-rolled templates for every new table. Trade-off is that it ships its own antd/React UI (bundled, no Node needed at install), so it won't match your Tailwind theme the way your hand-built dashboard does — for a white-labeled admin yours is the right call, for internal back-office it saves the "Day 4" entirely.

Either way, nice kit — the OTP email delivery pain in particular rang true.