GithubHelp home page GithubHelp logo

flavors / django-graphql-jwt Goto Github PK

View Code? Open in Web Editor NEW
812.0 812.0 170.0 1.11 MB

JSON Web Token (JWT) authentication for Graphene Django

Home Page: https://django-graphql-jwt.domake.io

License: MIT License

Python 99.64% Shell 0.36%
authentication django graphene graphql jsonwebtoken jwt oauth2 token

django-graphql-jwt's People

Contributors

abumalick avatar fivethreeo avatar frenchtoast747 avatar fschade avatar googol7 avatar jxltom avatar klebercode avatar lennartkerkvliet avatar mongkok avatar mr-asher avatar nerdoc avatar patryk-tech avatar rainshaw avatar sultaniman avatar valberg avatar vshelke avatar wiedi avatar wmai avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-graphql-jwt's Issues

Cannot query field "user" on type "ObtainJSONWebToken".

I try customize the ObtainJSONWebToken behavior.

# schema.py
import graphene
import graphql_jwt
from graphene_django.types import DjangoObjectType
from django.contrib.auth.models import User as UserModel


class ObtainJSONWebToken(graphql_jwt.JSONWebTokenMutation):
    user = graphene.Field(User)

    @classmethod
    def resolve(cls, root, info):
        return cls(user=info.context.user)


class Mutations(graphene.ObjectType):
    token_auth = ObtainJSONWebToken.Field()
    verify_token = graphql_jwt.Verify.Field()
    refresh_token = graphql_jwt.Refresh.Field()


schema = graphene.Schema(mutation=Mutations)

its work but I catch the Execption: Cannot query field "user" on type "ObtainJSONWebToken". Also autocompleting isn`t support

How to work without Django sessions?

Hi, thanks for your hard work on this. I was wondering how to properly use graphql_jwt without having to rely on cookies. And so I decided not to use sessions by disabling its middlewares.

my settings.py file as shown:

INSTALLED_APPS = [
    # 'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    # 'django.contrib.sessions',
    # 'django.contrib.messages',
    'django.contrib.staticfiles',
    'graphene_django',
    'myapp.core',
    'anymail'
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    # 'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    # 'django.contrib.auth.middleware.AuthenticationMiddleware',
    'graphql_jwt.middleware.JSONWebTokenMiddleware',
    # 'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

I am able to access all queries and mutations, including tokenAuth. but those wrapped in @login_required returns 'WSGIRequest' object has no attribute 'user' error.

curl 127.0.0.1/graphql/ \
    -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
.eyJlbWFpbCI6ImNoYXJsZXNAYmxhaC5jb20iLCJleHAiOjE1MzUwMDUzNzYsIm9yaWdfaWF0IjoxNTM1MDA1MDc2LCJmYl9pZCI6IiIsImZiX3Zlcml maWVkIjpmYWxzZX0.MmDosNAV6J6B7UOcDEmX4w18u5fepksp6wzscCCjsfE" \
    -H "Content-Type: application/json" \
    -X POST \
    -d '{"query":"query{user{id email}}"}'

{"errors":[{"message":"'WSGIRequest' object has no attribute 'user'","locations":[{"line":1,"column":7}],"path":["user"]}],"data":{"user":null}}

The schema for user query endpoint is defined as:

class Query(graphene.ObjectType):
    users = graphene.List(UserType)
    user = graphene.Field(UserType)

    def resolve_users(self, info, **kwargs):
        # TODO: protect this view from non admin/staff
        return User.objects.all()

    @login_required
    def resolve_user(self, info, **kwargs):
        return info.context.user

How to generate - access token vs refresh token

Hey,

I have a problem in understanding on how to generate properly access token and refresh token. According to the docs there is a concept of both due to JWT_REFRESH_EXPIRATION_DELTA and JWT_EXPIRATION_DELTA configuration variables, but in the source files I've seen only single method for generating a token which is https://github.com/flavors/django-graphql-jwt/blob/master/graphql_jwt/shortcuts.py#L4

The flow which I would like to follow is:

  1. User sign-up/sign-in to the app
  2. User receives two tokens: access_token (valid for 5 minutes) and refresh_token (valid for 7 days)
  3. The access_token is used to communicate frontend app with the backend API
  4. The refresh_token is used to regenerate the access_token in intervals, so it would be valid during using the app in longer session

Is it correct approach to such scenario?

access_token = get_token(user)
refresh_token = get_token(user, exp=datetime.utcnow() + settings.JWT_REFRESH_EXPIRATION_DELTA)

Override configuration settings with arguments

Currently, all the configuration is taken from environment variables, but I'd like to be able to override those values with custom settings dynamically. The reason for that is that I need to serve two different APIs from the same server:

  • public API exposing a limited set of data meant to be used by a public client application,
  • private API, protected with password and permission mechanisms meant to be used by an internal administration app.

In this scenario, I'd like to use two different JWT_AUDIENCE values for each API so that the tokens from the first one cannot be used in the second. I could achieve that by customizing JSONWebTokenMutation, token_auth decorator and passing extra data to get_token to override values from env in the payload.

@mongkok What are your thoughts on this? Would you be interested in including such feature in the library? If yes, I could work on a proof-of-concept PR.

Improve per-argument authentication flow

As far i know the middlewares in graphene are evaluated for each Resolver/Field.

Running through the JSONWebTokenMiddleware for each Field doesn't make sense in my eyes, because Authentication should take place per request.

Added Per-argument authentication

This should be optional in my opinion, because if you use the JSONWebTokenMiddleware as normal Middleware, you always get warnings like:

UserWarning: You do not have 'graphql_jwt.middleware.JSONWebTokenMiddleware' in your GRAPHENE['MIDDLEWARE'] setting. Please see the documentation for more information: https://github.com/flavors/django-graphql-jwt#installation mw_instance = middleware(handler)

My suggestion is to make 2 kinds of Middlewares:

  • Old default Middleware (for users which don't need the per argument authentication)
  • Inherit the new Middleware from the old one and add resolve() + warning

If you are interested i can make a PR

pip install does not give decorators described in docs

I have done both $ pip install django-graphql-jwt and $ pip install django-graphql-jwt==0.2.0

This is what the resulting decorators.py file looks like (and I point out what's missing in comments):

from functools import wraps

from django.contrib.auth import authenticate, get_user_model
from django.utils import six
from django.utils.translation import ugettext as _

from promise import Promise, is_thenable

from . import exceptions
from .refresh_token.shortcuts import create_refresh_token
from .settings import jwt_settings
from .shortcuts import get_token
from .utils import get_authorization_header

__all__ = [
    'user_passes_test',
    'login_required',
    'staff_member_required',
    'permission_required',
    'token_auth',
]


def context(f):
    def decorator(func):
        def wrapper(*args, **kwargs):
            info = args[f.__code__.co_varnames.index('info')]
            return func(info.context, *args, **kwargs)
        return wrapper
    return decorator


def user_passes_test(test_func):
    def decorator(f):
        @wraps(f)
        @context(f)
        def wrapper(context, *args, **kwargs):
            if test_func(context.user):
                return f(*args, **kwargs)
            raise exceptions.PermissionDenied()
        return wrapper
    return decorator


login_required = user_passes_test(lambda u: u.is_authenticated)
staff_member_required = user_passes_test(lambda u: u.is_active and u.is_staff)

### MISSING:
# superuser_required = user_passes_test(lambda u: u.is_active and u.is_superuser)

def permission_required(perm):
    def check_perms(user):
        if isinstance(perm, six.string_types):
            perms = (perm,)
        else:
            perms = perm

        if user.has_perms(perms):
            return True
        return False
    return user_passes_test(check_perms)


def token_auth(f):
    @wraps(f)
    def wrapper(cls, root, info, password, **kwargs):
        def on_resolve(values):
            user, payload = values
            payload.token = get_token(user, info.context)

            if jwt_settings.JWT_LONG_RUNNING_REFRESH_TOKEN:
                payload.refresh_token = create_refresh_token(user).token

            return payload

        username = kwargs.get(get_user_model().USERNAME_FIELD)

        if get_authorization_header(info.context) is not None:
            del info.context.META[jwt_settings.JWT_AUTH_HEADER_NAME]

        user = authenticate(
            request=info.context,
            username=username,
            password=password)

        if user is None:
            raise exceptions.JSONWebTokenError(
                _('Please, enter valid credentials'))

        if hasattr(info.context, 'user'):
            info.context.user = user

        result = f(cls, root, info, **kwargs)
        values = (user, result)

        if is_thenable(result):
            return Promise.resolve(values).then(on_resolve)
        return on_resolve(values)
    return wrapper

Am I crazy? It was just missing the one line that I was trying to use...

If I'm not crazy then (1) the docs and github have a different version than what is on pip or (2) someone is fking with me

Refresh tokens are stored in plain text

The refresh tokens introduced in v0.2.0 are stored in plain text in the DB. In case someone gets access to them, he/she can impersonate any user on the site.

Storing a hashed version of the refresh tokens, just like it is done with passwords, would make them useless for an attacker.

Anyways, thanks for your great work on this project!

graphql_jwt.middleware.JSONWebTokenMiddleware not found as middleware

I'm having issues with graphql_jwt.middleware.JSONWebTokenMiddleware not being found despite included in the middlewares. Any idea how I can troubleshoot or is this some issue with Django 2.1?

Django 2.1
Python 3.5.2
uWSGI 2.0.17
Ubuntu 16.04

In my settings.py:

GRAPHENE = {
    'SCHEMA': 'schema.schema',
    'MIDDLEWARE': [
            'graphql_jwt.middleware.JSONWebTokenMiddleware',
        ],
}

In uWSGI-log:

*** Starting uWSGI 2.0.17 (64bit) on [Tue Dec 11 10:48:00 2018] ***
compiled with version: 5.4.0 20160609 on 06 March 2018 16:38:25
os: Linux-4.4.0-139-generic #165-Ubuntu SMP Wed Oct 24 10:58:50 UTC 2018
nodename: 10xrecruit
machine: x86_64
clock source: unix
detected number of CPU cores: 2
current working directory: /etc/uwsgi/sites
detected binary path: /usr/local/bin/uwsgi
!!! no internal routing support, rebuild with pcre support !!!
chdir() to /var/www/qa-core
your processes number limit is 15733
your memory page size is 4096 bytes
 *** WARNING: you have enabled harakiri without post buffering. Slow upload could be rejected on post-unbuffered webservers *** 
detected max file descriptor number: 1024
lock engine: pthread robust mutexes
thunder lock: disabled (you can enable it with --thunder-lock)
uwsgi socket 0 bound to UNIX address /run/uwsgi/tenx_qa.sock fd 3
setgid() to 33
setuid() to 33
Python version: 3.5.2 (default, Nov 12 2018, 13:43:14)  [GCC 5.4.0 20160609]
PEP 405 virtualenv detected: /var/www/qa-core/venv
Set PythonHome to /var/www/qa-core/venv
Python main interpreter initialized at 0x2097060
python threads support enabled
your server socket listen backlog is limited to 100 connections
your mercy for graceful operations on workers is 60 seconds
mapped 250032 bytes (244 KB) for 4 cores
*** Operational MODE: preforking+threaded ***
/var/www/qa-core/venv/lib/python3.5/site-packages/django/core/handlers/base.py:36: UserWarning: You do not have 'graphql_jwt.middleware.JSONWebTokenMiddleware' in your GRAPHENE['MIDDLEWARE'] setting. Please see the documentation for more information: <https://github.com/flavors/django-graphql-jwt#installation>
  mw_instance = middleware(handler)
WSGI app 0 (mountpoint='') ready in 1 seconds on interpreter 0x2097060 pid: 16914 (default app)
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 16914)
spawned uWSGI worker 1 (pid: 16927, cores: 2)
*** Stats server enabled on /tmp/tenx_qa_stats.socket fd: 12 ***

Return User with ObtainJSONWebToken and token_auth decorator

currently the ObtainJSONWebToken mutation, specifically the @token_auth decorator, doesnt return the user with the token so you have to send two requests to get the user data after logging in.

Because token_auth is written as a decorator (an area I'm not super comfortable with) it makes it difficult to extend this mutation to get the user data, so you currently have to rewrite the entire decorator just to add payload.user = user in the on_resolve method inside it.

Authentication/Authorization for default resolvers

Hi,
I read the description on how to use @login_required and other decorators with resolvers. However, if one is not using explicit resolvers (instead using default ones), how can one enforce similar access controls?

In my case, I am using graphene with the Django User model. I have the following:

class UserNode(DjangoObjectType):
    class Meta:
        model = User
        filter_fields = ['first_name', 'last_name', 'id', 'email']
        interfaces = (Node, )

class Query(object):
    userNode = relay.Node.Field(UserNode)
    all_users = DjangoConnectionField(UserNode)

If I explicitly define a 'resolve_all_users' method and use the @login_required decorator on it, it works fine. But this (and other objects) in my schema are relying on default resolvers. How can I protect them without having to define resolvers explicitly?

I admit to being a novice in the use of graphene/graphql......and any help pointing me in the right direction is much appreciated.

token_auth set session cookie ?

Hi and thanks for your amazing work.

I had a look to your code and I don't understand why you need to use the django.contrib.auth.login inside the token_auth.

As explained by the django documentation, login() saves the user’s ID in the session, using Django’s session framework and I don't see why you need to store a cookie to save session information if you have a JWT token.

For me, the only case where such a thing can be useful is for the Server Side Rendering but the cookie needs to store the token instead of a session id.

Can you give me your point of view ?

Thanks in advance for your answer.

Implementation Example?

Hello! It's great to have this, I'm just a little confused on implementation. I've added the authentication backends settings, the middleware settings, and the verify/refresh token mutations added.

This is probably obvious, but how do I create a login mutation? This is what I have from the howtographql tutorial:

from django.contrib.auth.models import User
from django.contrib.auth import authenticate
from graphene import relay
from graphene_django import DjangoObjectType


class UserNode(DjangoObjectType):
    class Meta:
        model = User
        filter_fields = [
            'username',
        ]
        interfaces = (relay.Node, )


class LogIn(graphene.Mutation):
    user = graphene.Field(UserNode)

    class Arguments:
        username = graphene.String()
        password = graphene.String()

    def mutate(self, info, username, password):

        user = authenticate(
            username=username,
            password=password,
        )

        if not user:
            raise Exception('Invalid username or password')

        info.context.session['token'] = user.token
        return LogIn(user=user)


class Mutation(object):
        login = LogIn.Field()

Of course that's looking for the token property for a user, which is not found. Any thoughts? Forgive my newness to django/graphql!

Current user - always anonymus

I am looking for a solution to confirm the id of the logged in user and add it to the field in the forms.

class CreateLink(graphene.Mutation):
    id = graphene.Int()
    url = graphene.String()
    description = graphene.String()
    posted_by = graphene.Field(UserType)

    class Arguments:
        url = graphene.String()
        description = graphene.String()

    def mutate(self, info, url, description):
        user = info.context.user
        if user.is_anonymous:
            raise GraphQLError('Authentication credentials were not provided')

        link = Link(
            url=url,
            description=description,
            posted_by=user,
        )
        link.save()

        return CreateLink(
            id=link.id,
            url=link.url,
            description=link.description,
            posted_by=link.posted_by,
        )

But I still have information that the user is anonymous

Login with email and username

Hey! thanks for your great work
how can i override authenticate function for login, to the way that user can login with both username or email as username?

Can't get token wihen using custom Model.

Thank you for this project.

I am using a custom model with different params. (email and password, no username). However, I can't get it to work and I always get "Please, enter valid credentials" when providing email and password in my mutation.

My apologies, it was a bug on my side. Thank you for this project.

'ResolveInfo' object has no attribute 'path' in middleware

Whenever the server receives a request with the HTTP header 'Authorization':'JWT <token>' the response contains the error

'ResolveInfo' object has no attribute 'path'

Playing around with the debugger I noticed that the exception is raised from the 'resolve' method in the 'JSONWebTokenMiddleware' class of the middleware.py file.

The exception is caused by this piece of code:
field = getattr( info.schema, 'get_{}_type'.format(info.operation.operation), )().fields.get(info.path[0])
The exception says that 'info' does not have the attribute 'path'.
Playing with the debugger I noticed that the 'path' attribute exists inside the 'context' object. So I tried to change the code from info.path[0] to info.context.path[0] and everything started working correctly.

Since I'm fairly new to both Django and GraphQL I ask you: am I missing something here? Did I do something wrong in my code (i.e. url patterns, GraphQL types definitions, etc.) or in my settings/configurations that caused the info object to not have the expected structure? Is there something I can do to fix the problem? Obviously manually changing the library code is not the answer.

Thanks.

Raise custom exception with decorators

It would be nice if the decorators would support raising a custom exception (see line in decorators). I would like to implement a @verification_required annotation that would extend the @user_passes_test function to include checking the custom verification field on the User model. However, it currently raises the permission error, rather than my own error for verification.

https://github.com/flavors/django-graphql-jwt/blob/master/graphql_jwt/decorators.py#L40

Blacklist revoked tokens

Currently there is only one way to logout the user: Remove the jwt on client side and therefore a logout-now is not possible atm.

Storing the refresh token in the token will solve this problem, e.g:

{
  "username": "user123",
  "exp": 1544626946,
  "origIat": 1544626646,
  "refresh_token": "2ffee69532059e3569cf69f47d611e670b8fc0b5"
}

When a token gets revoked we add the token to the django cache:
cache.set(refresh_token.token, refresh_token.created + JWT_REFRESH_EXPIRATION_DELTA)

In the authentication backend we just check if the token is in the cache:

def authenticate(self, request=None, **kwargs):
        if request is None:
            return None

        token = get_credentials(request, **kwargs)

        if token is not None:
            if cache.get(token["refresh_token"]):
                return None
            return get_user_by_token(token, request)

        return None

What do you think about this?
We also need to build the blacklist cache on startup - a simple manage.py command should do this job

Configurable field instead of USERNAME_FIELD.

I'm having some difficulties with the username field as an unique identifier. I'm using email as username field in my project and superusers have the ability to change this value. When this happens the users token gets invalidated and will be logged out once he tries to refresh his token.

Other reason why it could by a good idea to pick another field is that this could lead to some security issues which are discussed in this issue.

Get rid of configuration with environment variables

I think that using environment variables to configure the application could be harmful. I have at least four reasons why I think it should be removed:

  1. As far as I know, Django has the only one official way to configure our applications—the settings.py file. I think it is beyond the responsibility of the third-party application to define alternative ways how this application can be configured.

  2. It will cause some hard-to-debug bugs or even vulnerabilities, as we’re talking about authentication, if some of the JWT_* variables have been defined somehow without the developers’ knowledge or because the developers don't expect that authentication may be configured somehow but settings.py because (see item 1) it is unnatural behavior for third-party apps.

  3. It also brings an unnecessary dependency in my project which I probably don’t want to have. Or maybe even the conflicts if my project requires a different version of django-environ.

  4. It is just unnecessary. If I wanted to configure JWT with env variables, I’d do it myself in the settings.py.

Deprecated middleware classes support

Hi again @mongkok

I have a proposition about old Django MIDDLEWARE_CLASSES support.
Because Django will be deprecated this old middleware style support in 2.0 version (check in link below)
Django 2.0 deprecation timeline

And still, many Django apps are used only Django 1.11
So when we want to use django-graphql-jwt, we must change MIDDLEWARE_CLASSES to MIDDLEWARE, but then few of other older apps, which still don't support Django 2.0 don't work.

I found a simple solution to this stalemate in django-cors-headers app.
check files:
https://github.com/ottoyiu/django-cors-headers/blob/master/corsheaders/middleware.py
and especially
https://github.com/ottoyiu/django-cors-headers/blob/master/corsheaders/compat.py

What do you think about adding this support?
Thank you for your great work :)

Automated testing example

Hello,

Would it be possible to have an example on how to tests the queries and mutations using this library ? At the moment, I haven't managed to test properly those because the @login_required is enabled and graphene cannot execute the requested queries on the schema. If you have an example of middleware to be used for the tests or any solutions I would be very interested.

Thanks in advance,

Server sends 200 response when secret key changes, not 401

When the server secret key changes all tokens are invalidated because they can no longer be decrypted. However, instead of returning a response with code 401-Unauthorized, the server returns a response with code 200-OK and includes the deserialization error in the response.

Example response:

{  
   "errors":[  
      {  
         "message":"Error decoding signature",
         "locations":[  
            {  
               "line":1,
               "column":38
            }
         ],
         "path":[  
            "createTeam"
         ]
      }
   ],
   "data":{  
      "createTeam":null
   }
}

If time allows, I'll follow this up with a PR to correct the issue.

How to implement Remember Me function?

Hi @mongkok .

Could you please help me a bit to understand how to implement Remember Me function?

As a reference I took your answer and my tests.

So, as mentioned in your answer, the next settings ...:

JWT_VERIFY_EXPIRATION=true
JWT_REFRESH_EXPIRATION_DELTA=days=7
JWT_EXPIRATION_DELTA=minutes=5

... means that you need to refresh every 5 mins and even you keep on refreshing token every 5 mins, you will still be logout in 7 days after the first token has been issued.

It's clear, but taking into account the aforesaid, I do not understand, how to achieve the next behavior:

  1. User clicks 'Remember me' (for example this function works 7 days, ie JWT_REFRESH_EXPIRATION_DELTA=days=7) and front-end stores received token in cookies or localStorage.
  2. User does not leave site for 30 mins. During this time token is refreshed (and stored in cookies or localStorage) 6+ times (ie JWT_EXPIRATION_DELTA=minutes=5).
  3. User closes site / browser.
  4. Next day user comes back, but with the settings mentioned above and with my tests the last refreshed token is not valid any more -> "Signature has expired", so, front-end should automatically logout user and ask user to log in again (despite the fact that the user ticked 'Remember me' and this function should keep user logged in for the next 6 days).

Initially I thought the refreshing, lets say every 5 mins, is used for security reason and I thought that all refreshed tokens actually lives until 7 days since the first token created and I wanted to use this for Remember Me function, but after my tests I see that token lives just 5 mins.

If set JWT_ALLOW_REFRESH to False, ie do not refresh token and let the first created token lives for the next 7 days, as I understand, this is insecure way.

Probably I do not see something. Could you help please?

Bug when using @login_required with mutation

Example mutation:

from graphql_jwt.decorators import login_required
...

class ChangeUsernameMutation(graphene.Mutation):
    user = graphene.Field(UserType)

    class Arguments:
        username = graphene.String()

    @login_required
    @transaction.atomic
    def mutate(self, info, username, **kwargs):
        user = info.context.user
        user.username = username
        user.save()
        return ChangeUsernameMutation(user=user)

Query:

mutation {
  changeUsername(username: "[email protected]") {
    user {
      username
    }
  }
}

Leads to:

{
  "errors": [
    {
      "message": "tuple.index(x): x not in tuple",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "changeUsername"
      ]
    }
  ],
  "data": {
    "changeUsername": null
  }
}

The bug is here:

info = args[f.__code__.co_varnames.index('info')]

Mutations use kwds instead of info (i don't know why)

This will fix it:

def context(f):
    def decorator(func):
        def wrapper(*args, **kwargs):
            try:
                info = args[f.__code__.co_varnames.index('info')]
            except ValueError:
                info = args[f.__code__.co_varnames.index('kwds')]
            return func(info.context, *args, **kwargs)
        return wrapper
    return decorator

User always Anonymous

Hey,

I can't seem to authenticate my user.
I have the following query

class Query(object):
    courses = graphene.List(CourseType)

    @login_required
    def resolve_courses(self, info, **kwargs):
        return Course.objects.all()

I've set the authentication backend and middleware correctly.
When I request this query, if I put an expired token I have the "Token expired" error.
When the token is valid, I have the message : "You do not have permission to perform this action" (because user is an AnonymousUser)

Any ideas what I do wrong ?

Migrations not up 2 date

when we run makemigrations a new migration in /graphql_jwt/refresh_token/migrations/0002_auto_20190123_1648.py gets created with the following content:

from django.db import migrations


class Migration(migrations.Migration):

    dependencies = [
        ('refresh_token', '0001_initial'),
    ]

    operations = [
        migrations.AlterModelOptions(
            name='refreshtoken',
            options={'verbose_name': 'refresh token', 'verbose_name_plural': 'refresh tokens'},
        ),
    ]

in ci we check if any uncreated migrations exist. if yes it stops the deployment. This happens because.

in https://github.com/flavors/django-graphql-jwt/blob/v0.2.0/graphql_jwt/refresh_token/migrations/0001_initial.py

'verbose_name': 'Refresh token', # uppercase, line 27
'verbose_name_plural': 'Refresh tokens', # uppercase, line 28

but in https://github.com/flavors/django-graphql-jwt/blob/v0.2.0/graphql_jwt/refresh_token/models.py

verbose_name = _('refresh token')  # lowercase, line 35
verbose_name_plural = _('refresh tokens') # lowercase, line 36

JSON Encoder

pyJWT exposes a JSON encoder argument when encoding the payload, I suggest this is made a setting without having to make an entirely new jwt_encode function. For example, my username field is a phone number which wasn't serializable into JSON.

def jwt_encode(payload, context=None):
    return jwt.encode(
        payload,
        jwt_settings.JWT_SECRET_KEY,
        jwt_settings.JWT_ALGORITHM,
        json_encoder=jwt_settings.JWT_JSON_ENCODER
    ).decode('utf-8')

Graphene backend with django-graphql-jwt honoring expired token

Using version 0.1.13 of django-graphql-jwt.

I am finding that the package is honoring expired tokens. I've used the Insomnia desktop client to verify. On the Django-side I am using a Graphene backend. To test, I used a simple graphql query, with a resolver that is protected using the @login_required decorator. The token I used in the header had been fetched 5 hours ago and should have expired 5 minutes later (I verified that by using jwt.io debugger to decode the token and determine the expiration time, then used the Epoch Converter at epochconverter.com to verify that the token did indeed expire 5 hours ago).

To make sure that the decorator is functional, I disabled the token in the header. And as expected the graphql query failed with:

{
	"errors": [
		{
			"message": "You do not have permission to perform this action",
			"locations": [
				{
					"column": 3,
					"line": 2
				}
			]
		}
	],
	"data": {
		"allUsers": null
	}
}

Also on the backend the following trace was logged, indicating that it was django-graphql-jwt package that was doing the authentication:

  File "/home/xxx/code/dev/local/lib/python2.7/site-packages/graphql/execution/executor.py", line 311, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "/home/xxx/code/dev/local/lib/python2.7/site-packages/graphql/execution/executors/sync.py", line 7, in execute
    return fn(*args, **kwargs)
  File "/home/xxx/code/dev/local/lib/python2.7/site-packages/graphene_django/debug/middleware.py", line 56, in resolve
    promise = next(root, info, **args)
  File "/home/xxx/code/dev/local/lib/python2.7/site-packages/graphql/execution/middleware.py", line 57, in make_it_promise
    return Promise.resolve(next(*a, **b))
  File "/home/xxx/code/dev/local/lib/python2.7/site-packages/graphene_django/fields.py", line 122, in connection_resolver
    iterable = resolver(root, info, **args)
  File "/home/xxx/code/dev/local/lib/python2.7/site-packages/graphql_jwt/decorators.py", line 27, in wrapper
    return func(info.context, *args, **kwargs)
  File "/home/xxx/code/dev/local/lib/python2.7/site-packages/graphql_jwt/decorators.py", line 39, in wrapper
    raise exceptions.PermissionDenied()
PermissionDenied: You do not have permission to perform this action

Have I got something misconfigured or is this a regression? I am using default django-graphql-jwt settings. I found this to also occur on 0.1.12.

Python2 support

Hi, there is any chances for python2 support?

Python2.7 and Django 1.11 has support until April 2020.
I'm sure that many large django 1.11 projects will stay with python2.7 for some time from now (including this used in my company).

I can create PR for this issue.

I want to erase token when logout

I want to erase token when logout. but I do not know how to do it.

Is it a design policy that uses JWT_VERIFY_EXPIRATION to create and erase repeatedly, just pretend to log out and invalidate token after 5 minutes? I am sorry I do not understand.

Or can you implement logout in other ways?

Unclear error in case of header authorization: "Network error: Response not successful: Received status code 401"

I propose to make this error more clear.

My debug:

  1. Lets add expired token to the header or even simple JWT 123;
  2. Do back-end call from front-end (Insomnia does not work as a front-end because request.user.is_anonymous is not triggered);
  3. Trace :)
    3.1 middleware.py: JSONWebTokenMiddleware.process_request() + user = authenticate(request=request) called;
    3.2 backend.py: JSONWebTokenBackend.authenticate() + return get_user_by_token(token) called;
    3.3 shortcuts.py: get_user_by_token() + payload = get_payload(token, context) called;
    3.4 utils.py: get_payload is called and raises either (depends on p1 above):
    • raise JSONWebTokenError(_('Signature has expired'))
    • raise JSONWebTokenError(_('Invalid token'));
  4. As a result user = authenticate(request=request) from p3.1 gets JSONWebTokenError and the code calls return JsonResponse({'errors': [{'message': str(err)}],}, status=401), which in the logs of back-end looks like:
django-container | Unauthorized: /api/graphql/v1
django-container | [09/Oct/2018 20:59:17] "POST /api/graphql/v1 HTTP/1.1" 401 53

or catch in case of front-end

tokenVerify (authToken) {
  this.$apollo.mutate({
    mutation: TOKEN_VERIFY,
    variables: {token: authToken}
  }).then((result) => {
    ...
  }).catch((error) => {
    console.log('Problem ->', error.message)  // Problem -> Network error: Response not successful: Received status code 401
  })
}

which is not useful at all.

I think it would be much much better to see real reason of the problem, which is either Signature has expired or Invalid token, which can be meaningfully handled.

how to implement logout

Hi

thanks a lot for coming up with this library, just wondering how to implement logout using django-graphql-jwt :)

Cheers

graphql_jwt.relay.ObtainJSONWebToken returns token when wrong credentials are submitted and Authorization header is set

I ran into a case when I had two users, A and B, and was sending a valid token of A when trying to obtain a new token for B. The mutation doesn't return any error, but instead returns a new token for A.

I dig a little in the code and I found out it was because of using authenticate here: https://github.com/flavors/django-graphql-jwt/blob/master/graphql_jwt/decorators.py#L69 , as the middleware will authenticate the user using the token instead of validating against the credentials.

So I ended up in a situation where, no matter what I was sending in the mutation input args, I was getting a valid token for another user.

I believe that mutation should validate the credentials instead of using all middlewares to authenticate the user.

A possible fix that pops in my mind right now would be calling authenticate only with username and password.

What do you think about this?

Thank you.

Protecting Mutation or Queries

This is a helpful project, I just have one question, is there a way to protect mutations and queries from unauthorized uses?

Documentation

The readme says
"Django-graphql-jwt reads your configuration from environment variables"
"and a single Django setting named GRAPHQL_JWT"

But doesn't clarify if I need both, which variables work on either side, which one takes precedence if there's a conflict.

It looks like maybe you're just looking in environment variables:

'JWT_EXPIRATION_DELTA': env.timedelta(

However, when I just set the environment variables:

export JWT_EXPIRATION_DELTA="minutes=1"
export JWT_VERIFY_EXPIRATION="true"
export JWT_REFRESH_EXPIRATION_DELTA="days=7"

that doesn't expire tokens for me.

Sorry, I'm sure I'm missing something obvious, but what exactly is required to enable jwt expiration?

Thanks!

Manually obtain JSON Web Token

Is it possible to manually obtain a JSON Web Token on the server-side? I'm trying to return the token in the mutation that creates a user.

I tried:

jwt_token = graphql_jwt.ObtainJSONWebToken(username=username, password=password)

but this did not work.

How to override these environment variables

Thanks a lot at first, with this awesome library, it is easy to support JWT in project, so awesome!

But I got a little question with these environment variables, how to override them? What if I wanna change algorithm to HS512. I tried a few ways, but all failed and no effects.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.