django-axes: Lockout returns 400 code instead of 403

Hi guys, I’ve tried to use django-axes with django rest framework. For some reason when a user reaches a limit of login attempts he continues receiving 400 code instead of 403. I was able to find a workaround in my view to return 403 but this probably should work out of the box, right?

I followed the documentation when I was installing the package.

After installing I specified a few addition settings:

AXES_COOLOFF_TIME = datetime.timedelta(minutes=env.int("AXES_COOLOFF_TIME", 1))
AXES_FAILURE_LIMIT = env.int("AXES_FAILURE_LIMIT", 2)
AXES_NEVER_LOCKOUT_GET = True
AXES_RESET_ON_SUCCESS = True

I use the latest (5.2.2) version of the package. Django version is 2.2.10 DRF version is 3.9.0

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 2
  • Comments: 23 (14 by maintainers)

Most upvoted comments

@solartune and others, please note that this can be resolved in 5.10.0 by attaching the correct signal handler for DRF

https://django-axes.readthedocs.io/en/latest/6_integration.html#integration-with-django-rest-framework

For DRF I use the following workaround. I do not install AxesMiddleware and instead connect to user_locked_out signal

from axes.signals import user_locked_out
from rest_framework.exceptions import PermissionDenied


def deny_access(**kwargs):
    raise PermissionDenied('Too many failed login attempts')

user_locked_out.connect(deny_access)

Hi guys, I’ve notices the same issue as @solartune with the DRF request and HttpRequest objects not being the same. So even though the axes_locked_out is correctly applied to the DRF request, this is not transferred to the HTTPRequest which is check in the middleware response path.

Additionally, I’m also having the issue that even though Axes correctly tag the DRF request with axes_locked_out==True, which subsequently breaks the iteration of backends (Axes-backend being the first), the request is still being passed on to the remaining authentication backends which successfully authenticate the request. This leaves the request in a state where it’s both “locked” and authenticated at once. Worth noting that I’m additionally using Djoser, which could be causing additional problems.

Expected behaviour would be that after multiple failed requests that triggers the lockout limit, a request with correct credentials should still not be passed to the subsequent authentication backends, but rather just be locked out independent of the credentials passed.

@solartune I think it would be awesome to support DRF correctly as well and I think your approach looks valid. I guess the two things to take into account are that Axes

  1. needs to always function “correctly” with stock Django configuration
  2. would be nice to function with DRF and different, commonly used plugins out-of-the-box

As long 1. is OK and we don’t degrade or “break” current functionality I’m all for trying out different approaches and seeing what works best!

I did some investigation recently. So, django request and drf request aren’t the same. It looks like drf request contains attributes of the django request but not vice versa. So, the attributes which set in the package to the drf request aren’t visible in the middleware where the django request is used.

Might be I missed smth but this is my current impression.