I am trying to implement Auth0 JWT-based authentication in my Django REST API using the django-rest-framework. I know that there is a JWT library available for the REST framework, and I have tried using it because the official Auth0 twitter account mentioned that it should work well with auth0 + Django.
EDIT: I am using the official auth0 python api guide for this code. It's written for flask, but I figured I could just port it to Django seeing as they work similarly.
Now, that didn't work out how I wanted to, so I am trying to write my own login_required
decorater for a view. The code I have here is as following:
def auth_required(f):
def wrap(request, *args, **kwargs):
auth = request.META.get('HTTP_AUTHORIZATION', None)
if not auth:
return authenticate({'code': 'authorization_header_missing', 'description': 'Authorization header is expected'})
parts = auth.split()
if parts[0].lower() != 'bearer':
return authenticate({'code': 'invalid_header', 'description': 'Authorization header must start with Bearer'})
elif len(parts) == 1:
return authenticate({'code': 'invalid_header', 'description': 'Token not found'})
elif len(parts) > 2:
return authenticate({'code': 'invalid_header', 'description': 'Authorization header must be Bearer + \s + token'})
token = parts[1]
try:
payload = jwt.decode(
token,
base64.b64decode(SECRET.replace("_","/").replace("-","+")),
audience=CLIENT_ID,
)
except jwt.ExpiredSignature:
return authenticate({'code': 'token_expired', 'description': 'token is expired'})
except jwt.InvalidAudienceError:
return authenticate({'code': 'invalid_audience', 'description': 'incorrect audience, expected: ' + CLIENT_ID})
except jwt.DecodeError:
return authenticate({'code': 'token_invalid_signature', 'description': 'token signature is invalid'})
return f(request, *args, **kwargs)
wrap.__doc__=f.__doc__
wrap.__name__=f.__name__
return wrap
Now, the authenticate()
is basically my custom implementation for Jsonify()
which is used in the documentation of Auth0 for Python API's. I have verified that this works, so that's not a problem.
SECRET
is my Auth0 secret, encoded in base64 (any other keys failed to decode)
CLIENT_ID
is my Auth0 client ID which is used as the audience, according to the Auth0 documentation.
I am using the Angular seed project on the frontend-side, and I have verified that the token indeed gets sent with the request, and I have verified that it's the exact same token that gets stored in the token
variable on the backend.
When jwt.decode()
is called, it will trigger the jwt.DecodeError
every time, and I have been spending countless hours trying to fix this, but I am absolutely stunned as to why this is not working. I have tried setting the JWT options to false, specifically the verify signature one. This worked, but I assume that it's unsafe to disable the verifying of the JWT signature.
I cannot figure out why this is failing me, I have tried this same code without it being in a decorator and it does the same thing. The view which is decorated is just an empty view which returns an OK HttpResponse.
Tldr; Using Django-REST + Auth0 JWT -- jwt.decode()
will not work no matter what I do.
EDIT2: It's worth mentioning I am corsheaders
for django-rest which allows me to make cross-domain requests. I have also followed the tip at the bottom of the Python API guide from Auth0 to uninstall and reinstall the JWT library, but this did nothing for me.
Am I overlooking something, is this implementation plain unsecure or do you have any better way to implement Auth0 with Django? Please let me know, this problem is causing me nightmares.
I had similar issues to do auth0 on my Django APIs, so I recently created a package to fix some of the issues: https://github.com/mcueto/djangorestframework-auth0
A classic case of "the hardest bugs to fix are usually the most stupid ones"...
I copied the secret key from the Auth0 dashboard by doubleclicking it, not realizing there were parts which didn't get copied. This fixed it.
If you want, you can use my custom decorator in your own project, to verify the JWT's.
You import it and then use it like so: