I've created a JWT-Authorised back end for an app. Login, logout, token retrieval and refresh all work fine, and as expected. I added a registration view this morning, which is throwing the usual "detail": "Authentication credentials were not provided.
error you'd expect for non-authenticated requests, as that's the default (see below).
However, because this is a registration endpoint, I don't want it to only allow authorised requests. (Having checked with a valid token, the rest of the view works as expected when you supply authentication.) Looking at the permissions section of the DRF docs, I thought that using the permission_classes wrapper with AllowAny would work here, but it hasn't.
What am I missing? I feel like the permission_classes decorator should override the default setting of 'IsAuthenticated'?
I'm testing on localhost from curl:
curl -X POST -H "Content-Type: application/json" -d '{"email":"[email protected]", "first_name": "boba", "last_name": "fett" "password":"xyz"}' http://localhost:8000/account/register/
View is:
@permission_classes(AllowAny)
@api_view(['POST'])
def register_user(request):
from django.contrib.auth.models import User
from rest_framework_jwt.views import obtain_jwt_token
if request.user.is_authenticated():
return Response ({"already_registered": "User with that username has already registered"}, status=status.HTTP_701_ALREADY_REGISTERED)
data = request.data
user, created = User.objects.get_or_create(username=data["email"],
email=data["email"],
first_name=data["first_name"],
last_name=data["last_name"],
password=data["password"])
if created:
token = obtain_jwt_token(data["email"],data["password"] )
return Response ({"token": token}, status=status.HTTP_200_OK)
else:
return Response ({"already_registered": "User with that username has already registered"}, status=status.HTTP_701_ALREADY_REGISTERED)
Permissions in settings.py are:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
}
Related questions: Django Rest Framework - Authentication credentials were not provided - I think the default permissions are correct, I just want to override them in this instance.
Django Rest Framework - DELETE ajax call failure due to incorrect CSFR token - CSRF not being used as JWT Based auth.
Django: Rest Framework authenticate header - Apache specific issue (currently still on devserver localhost)
Django Rest Framework Authentication credentials were not provided - Not yet answered!
TokenAuthentication. Note: The token authentication provided by Django REST framework is a fairly simple implementation. For an implementation which allows more than one token per user, has some tighter security implementation details, and supports token expiry, please see the Django REST Knox third party package.
Token authentication refers to exchanging username and password for a token that will be used in all subsequent requests so to identify the user on the server side.
The order of the decorators matter. There's also some problems with your code.
I recommend using a serializer, maybe something like below. If you want to use emails as username, I would make a custom User model. Django's default authentication system's username field has max_length of 30, and people's email addresses easily surpass that.
class UserSerializer(serializers.ModelSerializer):
first_name = serializers.CharField(required=False, allow_null=True)
last_name = serializers.CharField(required=False, allow_null=True)
class Meta:
model = User
fields = ('id', 'username', 'first_name', 'last_name', 'email', 'password')
def create(self, validated_data):
return User.objects.create_user(**validated_data)
@api_view(['POST'])
@permission_classes([permissions.AllowAny,])
def register_user(request):
if request.user.is_authenticated():
return Response({"already_registered": "User with that username has already registered"}, status=701)
data = request.data
serializer = UserSerializer(data=data, partial=True)
if serializer.is_valid():
serializer.save(username=serializer.validated_data['email'])
token = #call the url to get your tokens, use urllib or something similar
return Response({"token": token}, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Edit The ordering of decorators goes like this:
@decorator
@decorator2
def func():
print('hello world')
Is the same as decorator(decorator2(func)))
You have disabled permissions using @permission_classes
, but that's only the "authorization" part of "authentication and authorization". You need to disable the authentication handlers as well using @authentication_classes
in order to stop receiving a 401/403 error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With