flavors / django-graphql-jwt Goto Github PK
View Code? Open in Web Editor NEWJSON Web Token (JWT) authentication for Graphene Django
Home Page: https://django-graphql-jwt.domake.io
License: MIT License
JSON Web Token (JWT) authentication for Graphene Django
Home Page: https://django-graphql-jwt.domake.io
License: MIT License
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
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
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:
access_token
(valid for 5 minutes) and refresh_token
(valid for 7 days)access_token
is used to communicate frontend app with the backend APIrefresh_token
is used to regenerate the access_token
in intervals, so it would be valid during using the app in longer sessionIs it correct approach to such scenario?
access_token = get_token(user)
refresh_token = get_token(user, exp=datetime.utcnow() + settings.JWT_REFRESH_EXPIRATION_DELTA)
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:
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.
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:
resolve()
+ warningIf you are interested i can make a PR
Wondering if there are any similar projects for Flask users ? I would like my flask graphene-graphql app to have auth, ideally at per resource /model level or similar.
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
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!
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 ***
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.
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.
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.
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!
Some clarification regarding verifyToken when using long running refresh tokens would be nice here:
How to get user identity? just like flask-jwt-extended method get_jwt_identity
Is it possible to identify an anonymous website user by an anonymous session id?
Currently refresh tokens are created on each login. It would be great to only generate them when the refreshToken field was requested.
Would that make sense as a PR?
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
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?
The only error I get, but that's because of my code:
graphql.error.located_error.GraphQLLocatedError: 'WSGIRequest' object has no attribute 'user'
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.
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.
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
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
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.
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:
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.
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.
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.
It is just unnecessary. If I wanted to configure JWT with env variables, I’d do it myself in the settings.py
.
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 :)
As mentioned in this article (https://dev.to/rdegges/please-stop-using-local-storage-1i04), storing sensitive data in localstorage is a bad idea.
I think it would be good to allow the JWT token to be stored in a cookie with the "httpOnly" and "secure" flags to counter XSS attacks.
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,
It seems the token is still valid after changing password
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.
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:
JWT_REFRESH_EXPIRATION_DELTA=days=7
) and front-end stores received token in cookies or localStorage.JWT_EXPIRATION_DELTA=minutes=5
).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?
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:
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
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 ?
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.
'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
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')
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.
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.
Right now we need to have GRAPHENE.SCHEMA
defined to run the tests, could we be allowed to pass the schema to the client too?
Has anyone tried using django-defender or some login throttle library?
Seems like one would need to modify decorators.py's token_auth to use custom logic like the example here: https://github.com/kencochrane/django-defender#adapting-to-other-authentication-method
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?
I propose to make this error more clear.
My debug:
JWT 123
;request.user.is_anonymous
is not triggered);JSONWebTokenMiddleware.process_request()
+ user = authenticate(request=request)
called;JSONWebTokenBackend.authenticate()
+ return get_user_by_token(token)
called;get_user_by_token()
+ payload = get_payload(token, context)
called;get_payload
is called and raises either (depends on p1 above):
raise JSONWebTokenError(_('Signature has expired'))
raise JSONWebTokenError(_('Invalid token'))
;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.
Hi
thanks a lot for coming up with this library, just wondering how to implement logout using django-graphql-jwt :)
Cheers
I understand cases when we need to verify/refresh token which differs from the header, but is there any real reason to send the same token in the header and in the query in order to verify/refresh it?
I think graphql-jwt should check whether token in query is provided and if no, should take it from the header and verify/refresh it (via get_authorization_header
).
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.
This is a helpful project, I just have one question, is there a way to protect mutations and queries from unauthorized uses?
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:
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!
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.
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.