Django · Email breach checks
Check email breaches in a Django signup form
This example extends UserCreationForm with an email field and runs the Finowl lookup in clean_email, the hook Django calls after the field's own validation. A 400 response becomes a form error, because Finowl only accepts plain addresses with a dotted domain. Any other failure is logged and ignored so signups keep working. Breaches whose DataClasses include Passwords are collected with their Title and BreachDate for later use.
The view does not block registration when a breach is found. Instead it saves the user, logs them in and adds a warning through the messages framework that names the breaches which leaked passwords and suggests a unique password. That keeps the decision with the user while still acting on the result. If you prefer, move the lookup into a Celery task triggered after the user is created.
Steps
- 1
Save the helper module as accounts/finowl.py and set FINOWL_API_KEY in your environment.
- 2
Add SignupForm to accounts/forms.py and route the signup view in urls.py.
- 3
Render messages in your base template so the breach warning appears after the redirect.
- 4
Sign up with an address you control and confirm the warning lists breach titles and dates.
pip install django requests# accounts/forms.py
import logging
from django import forms
from django.contrib.auth.forms import UserCreationForm
from .finowl import FinowlError, finowl_post # helper from the Django integration page
logger = logging.getLogger(__name__)
class SignupForm(UserCreationForm):
email = forms.EmailField(max_length=254)
class Meta(UserCreationForm.Meta):
fields = ("username", "email")
def clean_email(self):
email = self.cleaned_data["email"].strip().lower()
self.exposed_in = []
try:
result = finowl_post("/v1/breaches/email", {"email": email}, timeout=5)
except FinowlError as exc:
if exc.status == 400:
raise forms.ValidationError("Enter a plain address such as name@example.com.")
if exc.status == 401:
logger.error("Finowl rejected FINOWL_API_KEY; skipping breach check")
elif exc.status == 429:
logger.warning("Finowl %s; retry after %ss", exc.code, exc.retry_after)
return email # an unavailable check never blocks a signup
for breach in result["breaches"]:
if "Passwords" in breach["DataClasses"]:
self.exposed_in.append(f"{breach['Title']} ({breach['BreachDate']})")
return email
# accounts/views.py
from django.contrib import messages
from django.contrib.auth import login
from django.shortcuts import redirect, render
def signup(request):
form = SignupForm(request.POST or None)
if request.method == "POST" and form.is_valid():
user = form.save()
login(request, user)
if form.exposed_in:
messages.warning(
request,
"Your email appeared in breaches that leaked passwords: "
+ ", ".join(form.exposed_in)
+ ". Use a password you have never used anywhere else.",
)
return redirect("home")
return render(request, "accounts/signup.html", {"form": form})IN PRODUCTION
Production tips
Keep the timeout short in form validation. Five seconds is already a long wait on a signup page, and the helper raises FinowlError on timeouts so clean_email can carry on without the result.
Avoid revealing breach details for addresses the visitor has not proven they own. Showing the warning after email verification keeps the form from becoming a way to research other people's addresses.
Cache lookups per normalized email with Django's cache framework, so a user who resubmits the form after a password error does not spend a second credit on the same address.
Each lookup costs 1 credit, with 100 free credits every month. See pricing and error codes.
A FEW GOOD QUESTIONS
Common questions
Related guides
- Breached password validator for Django
- Batch gender enrichment with a Django command
- Check email breaches in Python with requests
- Check email breaches in Node.js with fetch
- Check email breaches in TypeScript with typed results
- Check email breaches in Go with net/http
- Email breach checks for fintech signups and logins
- Email breach checks for online stores and shopper logins
- Email breach checks for SaaS logins and admins
Check email breaches in a Django signup form
Grab an API key, paste the example and run it. 100 free credits every month.