Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

django-axes compatibility #712

Closed
aleksihakli opened this issue Apr 27, 2019 · 12 comments · Fixed by #949
Closed

django-axes compatibility #712

aleksihakli opened this issue Apr 27, 2019 · 12 comments · Fixed by #949

Comments

@aleksihakli
Copy link
Member

aleksihakli commented Apr 27, 2019

A user recently reported an issue with using django-oauth-toolkit with django-axes in jazzband/django-axes#426

The problem is that the request object offered by the django-oauth-toolkit does not seem to be a Django HttpRequest object with the request.META and other necessary HttpRequest attributes. Is there an ideal way to offer a solution for compatibility?

@nickdjones
Copy link

nickdjones commented May 19, 2019

So I did manage to get this working in the end, I managed to do it by editing just the custom validator for DOT and it's an extremely dirty hack so this is more if anyone else is having the same issue rather than any sort of permanent fix:

from axes.helpers import get_client_ip_address, get_client_user_agent
from django.contrib.auth import authenticate
from django.http import HttpRequest, QueryDict
from oauth2_provider.oauth2_validators import OAuth2Validator


class MyOAuth2Validator(OAuth2Validator):
    def validate_user(self, username, password, client, request, *args, **kwargs):
        """
        Check username and password correspond to a valid and active User
        """
        old_request = request
        if request and not isinstance(request, HttpRequest):
            request = HttpRequest()
            request.uri = old_request.uri
            request.method = request.http_method = old_request.http_method
            request.META = request.headers = old_request.headers
            request._params = old_request._params
            request.decoded_body = old_request.decoded_body
            request.axes_ip_address = get_client_ip_address(request)
            request.axes_user_agent = get_client_user_agent(request)
            qdbody = QueryDict(str(old_request.body), mutable=True)
            if request.method == 'GET':
                request.GET = qdbody
            elif request.method == 'POST':
                request.POST = qdbody

        u = authenticate(request=request, username=username, password=password)
        if u is not None and u.is_active and hasattr(u, 'profile'):
            request = old_request
            request.user = u
            return True
        return False
OAUTH2_PROVIDER = {
    'OAUTH2_VALIDATOR_CLASS': 'custom.app.MyOAuth2Validator',
    'SCOPES': {'read': 'Read scope', 'write': 'Write scope'},
}

@auvipy
Copy link
Contributor

auvipy commented May 19, 2019

oAuth is designed to be worked with django rest framework etc.

@aleksihakli
Copy link
Member Author

It would be ideal if the request objects it uses had the standard Django attributes so that other tools and packages could function better with them.

The request=None kwarg was added to the django.contrib.auth.authenticate a while back and people might have custom authentication backends that utilize it.

Would it be possible to add the request kwarg to the default authentication flow and add the standard Django request attributes to it? Specifically request.GET, request.POST, and request.META.

I've added a simple example on how to customize a compatible validator for the OAuth toolkit on the Axes docs, but I think updating the stock OAuth validator in the toolkit project could benefit multiple users in the long run.

@auvipy
Copy link
Contributor

auvipy commented May 19, 2019

this package is only needed with rest based works i guess. id you need django specific then django all auyh woud be a better choice with core django without drf

@nickdjones
Copy link

nickdjones commented May 19, 2019

@auvipy I don't think you understand what @aleksihakli is saying, django-axes is a user login security tool that inspects the request object at various points in the process and because django-oauth-toolkit doesn't use a standard django HttpRequest() it isn't possible to track API logins. It's not about changing the functionality, it's about django-oauth-toolkit and django-axes working together so that API logins can be tracked as well as website logins.

@nickdjones
Copy link

nickdjones commented May 19, 2019

Also @aleksihakli your example validators.py doesn't work, the oauthlib.Request() class used by django-oauth-toolkit is hardcoded to error when attributes it doesn't expect are accessed https://github.com/oauthlib/oauthlib/blob/master/oauthlib/common.py#L436 so you can't add arbitrary fields to it. The example I pasted above is the only way I could get it working, by recreating a fake HttpRequest() object.

@aleksihakli
Copy link
Member Author

aleksihakli commented May 20, 2019

Oh, sorry about that!

Let's try and get the example in the docs up to date then. Would you like to open a PR for the Axes docs/x_integration.rst? I can also try and update a functioning example, but I don't have a suitable test bench for checking if it works.

Are you talking about the request object when you refer to response by the way? I'm trying to verify that we are talking about the same thing. What is the exact type of the object in the runtime?

@nickdjones
Copy link

nickdjones commented May 20, 2019

Sorry yes it is request - wrote that in a rush! OAuthLib returns oauthlib.Request() and my solution fakes a Django HttpRequest() so the attributes can be accessed properly through authenticate(). The correct oauthlib.Request() is then returned from the validator so that django-oauth-toolkit continues to function (edited my comment). I'll try and get a PR sent over today.

@aleksihakli
Copy link
Member Author

aleksihakli commented May 20, 2019

Great! You can build the docs in your own environment with cd docs && make html by the way. Just install requirements with pip first, that should setup Sphinx correctly.

RTD builds the docs automatically from each commit to master in upstream so adding examples does not require a new release.

aleksihakli added a commit to jazzband/django-axes that referenced this issue May 30, 2019
Use example from jazzband/django-oauth-toolkit#712
for reference with props to @HCNick for debugging
simanto604newscred pushed a commit to simanto604newscred/django-axes that referenced this issue Jul 31, 2019
Use example from jazzband/django-oauth-toolkit#712
for reference with props to @HCNick for debugging
simanto604newscred pushed a commit to simanto604newscred/django-axes that referenced this issue Jul 31, 2019
Use example from jazzband/django-oauth-toolkit#712
for reference with props to @HCNick for debugging
@adamchainz
Copy link
Contributor

#643 should fix one error I'm seeing with axes logging errors due to missing request.

aleksihakli added a commit to jazzband/django-axes that referenced this issue Nov 13, 2019
Inspired by jazzband/django-oauth-toolkit/issues/712
and solution by @HCNick for more functional authentication
@adamchainz
Copy link
Contributor

#643 is the wrong fix and the cause of this problem, my bad. Broke production for us.

@ShaheedHaque
Copy link
Contributor

We just hit this. Isn't the correct fix for this issue, and also #636, and #808 the patch in #712 (comment)? IIUC:

Finally, it seems to me that a patch like the one above (but possibly without the two axes-specific attributes?) could be applied directly to DOT code, and would work to improve the compatibility of DOT. I'd be happy to author a PR if that would help.

ShaheedHaque added a commit to ShaheedHaque/django-oauth-toolkit that referenced this issue Mar 22, 2021
compatibiity with more backends. Resolves jazzband#712. Resolves jazzband#636.
Resolves jazzband#808.
ShaheedHaque added a commit to ShaheedHaque/django-oauth-toolkit that referenced this issue Mar 22, 2021
compatibiity with more backends. Resolves jazzband#712. Resolves jazzband#636.
Resolves jazzband#808.
ShaheedHaque added a commit to ShaheedHaque/django-oauth-toolkit that referenced this issue Mar 22, 2021
compatibiity with more backends. Resolves jazzband#712. Resolves jazzband#636.
Resolves jazzband#808.
n2ygk added a commit that referenced this issue Mar 22, 2021
…biity with more backends. (#949)

* Provide django.contrib.auth.authenticate() with a request for
compatibiity with more backends. Resolves #712. Resolves #636.
Resolves #808.

Co-authored-by: Alan Crosswell <[email protected]>
wannacfuture added a commit to wannacfuture/Tuto_Django that referenced this issue Jun 1, 2023
Use example from jazzband/django-oauth-toolkit#712
for reference with props to @HCNick for debugging
wannacfuture added a commit to wannacfuture/Tuto_Django that referenced this issue Jun 1, 2023
Inspired by jazzband/django-oauth-toolkit/issues/712
and solution by @HCNick for more functional authentication
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants