jazzband / django-axes

Keep track of failed login attempts in Django-powered sites.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

BUG: Setting `AXES_USERNAME_FORM_FIELD` to a custom value fails to fill the username field for `AccessAttempt`

jur-clerkx opened this issue · comments

Describe the bug
In our project, we have set the AXES_USERNAME_FORM_FIELD to a custom value. When doing this, AccessAttempt in the database doesn't get a username set, this stays None.

After a quick debug, it showed that there is a call being made where the credentials are given. However, in the credentials dict, the username is stored in a field named username in stead of the value set in AXES_USERNAME_FORM_FIELD. Since the code tries to fetch the username based on the name in the settings, this will return None. Assumption for now is that this were it goes wrong.

To Reproduce
Steps to reproduce the behavior:

  1. Create a custom login form with a username field that has a custom name (e.g. auth-username)
  2. Set the AXES_USERNAME_FORM_FIELD to auth-username
  3. Do a failed login attempt
  4. The username field on the AccessAttempt will be empty.

Expected behavior
The username field on the AccessAttempt model is filled with the correct username.

Your environment
python version: 3.8.18
django version: 3.2.23
django-axes version: 6.3.0
Operating system: Mac OS

Additional context
What was interesting was that the credentials that were shown in the debugger was:
username: username filled in the form
password: ***********************

Possibly this conflicts with the logging implementation.

Possible implementation
Probably an easy bugfix where username is used in stead of the value set in AXES_USERNAME_FORM_FIELD

So after some digging, it turns out that the code tries to fetch the username out of the Django login failed signal by using the AXES_USERNAME_FORM_FIELD as key. However, in our case, Django always uses the username key. Probably need to use the USERNAME_FIELD setting here?

Good catch. Would someone like to open a PR that changes the username field to a the more appropriate AXES_USERNAME_FORM_FIELD or USERNAME_FIELD value?

I managed to trace it down to where the code conflicts with the data that is sent by the the Django user_login_failed signal:

username = get_client_username(request, credentials)

The get_client_username method will try to fetch the username from the credentials using the AXES_USERNAME_FORM_FIELD setting. However, the Django signal will use the regular name of the field used as username (username, e-mail etc).
I'll try to make a PR to fix this, probably by just preferring to look in the request for the username in stead of the credentials.

For people who are also having this issue, you can implement a workaround by using the AXES_USERNAME_CALLABLE setting:

AXES_USERNAME_CALLABLE = lambda request, _: request.POST.get("<your username field>", None)  # noqa