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

AuthenticationMiddleware must be installed to access request.user #6

Open
erikdewildt opened this issue Feb 2, 2021 · 6 comments
Open

Comments

@erikdewildt
Copy link

erikdewildt commented Feb 2, 2021

Hi,

First of all thanks for your package!

I can get the JTW token authorisation working with normal queries. However when decorate the resolver method of a subscription with login_required decorator i run into an error:

Traceback (most recent call last):
File "/Users/erik/.virtualenvs/firewallautomation/lib/python3.9/site-packages/graphql/execution/execute.py", line 623, in resolve_field_value_or_error
result = resolve_fn(source, info, **args)
File "/Users/erik/.virtualenvs/firewallautomation/src/ariadne-jwt/ariadne_jwt/decorators.py", line 28, in wrapper
return func(info.context, *args, **kwargs)
File "/Users/erik/.virtualenvs/firewallautomation/src/ariadne-jwt/ariadne_jwt/decorators.py", line 40, in wrapper
if test_func(context.get('request').user):
File "/Users/erik/.virtualenvs/firewallautomation/lib/python3.9/site-packages/starlette/requests.py", line 146, in user
assert (
AssertionError: AuthenticationMiddleware must be installed to access request.user
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/erik/.virtualenvs/firewallautomation/lib/python3.9/site-packages/graphql/execution/execute.py", line 340, in execute_operation
result = (
File "/Users/erik/.virtualenvs/firewallautomation/lib/python3.9/site-packages/graphql/execution/execute.py", line 433, in execute_fields
result = self.resolve_field(
File "/Users/erik/.virtualenvs/firewallautomation/lib/python3.9/site-packages/graphql/execution/execute.py", line 604, in resolve_field
return self.complete_value_catching_error(
File "/Users/erik/.virtualenvs/firewallautomation/lib/python3.9/site-packages/graphql/execution/execute.py", line 681, in complete_value_catching_error
self.handle_field_error(error, field_nodes, path, return_type)
File "/Users/erik/.virtualenvs/firewallautomation/lib/python3.9/site-packages/graphql/execution/execute.py", line 696, in handle_field_error
raise error
File "/Users/erik/.virtualenvs/firewallautomation/lib/python3.9/site-packages/graphql/execution/execute.py", line 667, in complete_value_catching_error
completed = self.complete_value(
File "/Users/erik/.virtualenvs/firewallautomation/lib/python3.9/site-packages/graphql/execution/execute.py", line 733, in complete_value
raise result
File "/Users/erik/.virtualenvs/firewallautomation/lib/python3.9/site-packages/graphql/execution/execute.py", line 623, in resolve_field_value_or_error
result = resolve_fn(source, info, **args)
File "/Users/erik/.virtualenvs/firewallautomation/src/ariadne-jwt/ariadne_jwt/decorators.py", line 28, in wrapper
return func(info.context, *args, **kwargs)
File "/Users/erik/.virtualenvs/firewallautomation/src/ariadne-jwt/ariadne_jwt/decorators.py", line 40, in wrapper
if test_func(context.get('request').user):
File "/Users/erik/.virtualenvs/firewallautomation/lib/python3.9/site-packages/starlette/requests.py", line 146, in user
assert (
graphql.error.graphql_error.GraphQLError: AuthenticationMiddleware must be installed to access request.user

The subscriptions is build using the Django-channels setup in the documentation. Both MIDDLEWARE and AUTHENTICATION_BACKENDS are properly configured.

Debugging shows that the code is trying to use context.get('request').user.

When i don't use the login_required decorator and have a look at the info.context["request"] using a breakpoint in the resolver the request is a starlette.websockets.WebSocket object.

Accessing the user property of this WebSocket object results in the same AssertionError: AuthenticationMiddleware must be installed to access request.user error.

Any advice on how to get the login_required decorator working with a subscription resolver method?

Thanks in advance!
Regards,

Erik

@Usama0121
Copy link
Owner

@erikdewildt can you share sample code to reproduce the error.

@sebastian-chang
Copy link

@Usama0121 I'm getting the same thing that @erikdewildt is getting. At least for me I believe it has something to do with my asgi file. This is my resolver with login required.

@convert_kwargs_to_snake_case
@sync_to_async
@login_required
def list_room_messages(object, info, room, **kwargs):
    print("WHAT", info.context.get('request').user)
    return [
        {
            "id": message.id,
            "text": message.text,
            "time": message.timestamp,
            "user": message.user,
            "room": message.room,
        }
        for message in Message.objects.filter(room=room)
    ]

In my asgi file I have tried application = GraphQL(schema) as well as

application = URLRouter(
    [
        path(
            "graphql/",
            GraphQL(
                schema,
                debug=True,
            ),
        ),
        path("", AsgiHandler()),
    ]
)

Both end up giving my the following error.

File "/Users/schang/.local/share/virtualenvs/Backend-_hQa2JEQ/lib/python3.9/site-packages/starlette/requests.py", line 146, in user
    assert (
graphql.error.graphql_error.GraphQLError: AuthenticationMiddleware must be installed to access request.user

If I revert back to application = get_asgi_application() it seems to work but then I'm not able to do async resolvers.

@Usama0121
Copy link
Owner

Usama0121 commented Feb 2, 2021

The following code resolves the AuthenticationMiddleware error but currently, ariadne_jwt does not support this middleware style

import os

from ariadne.asgi import GraphQL
from channels.routing import URLRouter
from channels.auth import AuthMiddlewareStack
from django.core.asgi import get_asgi_application
from django.urls import path, re_path

from .schema import schema

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project_1.settings')

application = (AuthMiddlewareStack(URLRouter([
    path('graphql/', GraphQL(schema=schema), name='graphql'),
    re_path(r"", get_asgi_application())
])))

@sebastian-chang
Copy link

Now I can't get a user token on my login mutation. Getting the error.

 auth = request.META.get(jwt_settings.JWT_AUTH_HEADER, '').split()
graphql.error.graphql_error.GraphQLError: 'Request' object has no attribute 'META'

@Usama0121
Copy link
Owner

@sebastian-chang as I mentioned in my last comment jwt authentication is currently not supported. This is mainly because jwt authentication relies on request headers whereas channels use web socket which does not process headers in a supported way. A separate middleware is required to handle auth via channels

@erikdewildt
Copy link
Author

Thanks for your answers, some pointers to a solution where given in this ticket:
mirumee/ariadne#472

As @Usama0121 also mentioned some kind of custom middleware is required.

I've tried to apply the AuthMiddlewareStack in in my routing.py:

application = ProtocolTypeRouter(
    {
        "http": URLRouter(
            [
                re_path(r"", get_asgi_application()),
            ]
        ),
        "websocket": AuthMiddlewareStack(
            URLRouter(
                [
                    path("firewall_requests/graphql/", GraphQL(schema, debug=True)),
                ]
            )
        ),
        "channel": ChannelNameRouter(
            {
                "mail": MailConsumer.as_asgi(),
                "garbage-collect": GarbageCollect.as_asgi(),
            }
        ),
    }
)

This allows for me to access a user in the request scope. I've tried to define a simple permission_required mixin to do so (which now only prints the user):

def permission_required(permission=None):
    def decorator_permission_required(function):
        @functools.wraps(function)
        async def wrapper_permission_required(obj, info):
            user = await channels.auth.get_user(info.context["request"].scope)
            print(user)
            return function(obj, info)

        return wrapper_permission_required

    return decorator_permission_required

When I apply this decorator to the subscription generator function a user can be resolved when I subscribe using the playground. However this is not the user for which the JWT token is set in the HTTP Headers as these are not used in the subscriptions. It seems to be the user which currently has a HTTP Session in the same browser towards the Django backend. If I did not open up a session first towards the Django backend a AnonymousUser is resolved.

As a workaround I was thinking of defining a 'JWT' argument to the subscription and then resolve the user from the JWT token. When I have the user belonging to the token I can then validate on permissions. For me it would be acceptable to use a argument in the subscription to provide the JWT token.

Any thoughts on that?

Did not investigate further, will dive into it tomorrow again, but perhaps you have any pointers on how to resolve the JWT token back to a user instance?

Thanks again,

Regards,
Erik

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants