I ended up doing this in case anyone has the same question.
class UserNotActivatedError(Exception):
pass
class UserLockedError(Exception):
def __init__(self, remaining_mins):
self.remaining_mins = remaining_mins
# backend
def authenticate(self, email=None, password=None):
if email is None or password is None:
return None
try:
user = ExtUser.objects.get(email=email)
user.last_login_attempt_at = timezone.now()
if not use.is_active:
raise UserNotActivatedError
if user.is_locked:
# Check when it was locked and found the duration
sec_to_go = user.get_remaining_locktime()
if sec_to_go:
raise UserLockedError(sec_to_go)
if user.check_password(password):
user.last_login_at = timezone.now()
return user
else:
return None
except User.DoesNotExist:
return None
Then in the login form you can catch these errors and pass on appropriate validation errors to the view.