I need to display an error message on failed login attempts that says whether the user was inactive, the password was incorrect, or the user was not found. I’m authenticating against an API so I made a custom authentication backend. At the moment, to prove to the client login is working, I have it raising exceptions called UserNotFoundError, InactiveUserError, and InvalidPasswordError.
I’m fairly sure that’s not the right way to go about it, and if I want to use Django’s auth views I have no way to catch those exceptions anyway.
I assume I will need to create my own auth views. But, is there a better way?
(I know it’s not as secure to give a reason for a failed login, but I have to.)
This is totally untested, and not guaranteed to work, but you could try something along these lines. The only caveat with this solution is that your backend must be the last authentication backend used (or the only one used).
The contrib.auth.views.authenticate method takes an argument for authentication_form, meaning that you should be able to override it with your own form that extends contrib.auth.forms.AuthenticationForm. That form is what calls your backend’s authenticate() method. So, in your backend you could return some custom object which contains both the user instance as well as a “failed” status and a “failed reason” for why authentication failed.
If your custom object has a failed status, you can throw a ValidationError with the exception, at which point the login view will not do any further processing of the user.
Also make sure you override form.get_user() so that it returns the actual User instance, and not your custom object.