0

I am stuck for a few days with this, and nothing is working for me.

So, my db is in Neo4j, and I am making API with Django DRF. These are the relevant files:

models.py

    class NeoUser(StructuredNode):
        uid = UniqueIdProperty()
        emp_id = StringProperty(unique_index=True)
        username = StringProperty()
        email = EmailProperty(unique_index=True)
        password = StringProperty()
        designation = StringProperty()
        department = StringProperty()
        pp = StringProperty(unique_index=True)
        dob = DateTimeProperty()
        phone = StringProperty(unique_index=True)

settings.py

        REST_FRAMEWORK = {

        'DEFAULT_PERMISSION_CLASSES': [
            'rest_framework.permissions.IsAuthenticated',
        ],
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework_simplejwt.authentication.JWTAuthentication',
        ),
    }

    SIMPLE_JWT = {
        # 'JWT_SECRET_KEY': 'your-secret-key',
        'JWT_ALGORITHM': 'HS256',
        'JWT_ALLOW_REFRESH': True,
        'ACCESS_TOKEN_LIFETIME': timedelta(days=30),
        'REFRESH_TOKEN_LIFETIME': timedelta(days=90),
        'JWT_AUTH_HEADER_PREFIX': 'Bearer ',
        "ROTATE_REFRESH_TOKENS": False,
        "BLACKLIST_AFTER_ROTATION": False,
        "UPDATE_LAST_LOGIN": False,

        "ALGORITHM": "HS256",
        "SIGNING_KEY": SECRET_KEY,
        # "VERIFYING_KEY": SECRET_KEY,
        "AUDIENCE": None,
        "ISSUER": None,
        "JSON_ENCODER": None,
        "JWK_URL": None,
        "LEEWAY": 0,
        'JWT_ALLOW_REFRESH': True,

        # 'AUTH_HEADER_TYPES': ('Bearer',),
        "AUTH_HEADER_NAME": "Authorization",
        "USER_ID_FIELD": "id",
        "USER_ID_CLAIM": "user_id",
        "USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",

        "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
        "TOKEN_TYPE_CLAIM": "token_type",
        "TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",

        "JTI_CLAIM": "jti",

        # "SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
        # "SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),
        # "SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),

        "TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
        "TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",
        "TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",
        "TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer",
        "SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer",
        "SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",

    }

serializer.py

    from rest_framework import serializers
    from .models import NeoUser
    from rest_framework.validators import UniqueValidator

    class Neo4jUserSerializer(serializers.Serializer):
        username = serializers.CharField()
        emp_id = serializers.CharField()
        email = serializers.CharField()
        phone = serializers.CharField()
        password = serializers.CharField()

        def validate(self, data):
            id = data.get('uid')
            username = data.get('username')
            email = data.get('email')
            emp_id = data.get('emp_id')
            phone = data.get('phone')

            # Check if the username or email already exist in Django User or NeoUser
            if NeoUser.nodes.filter(username=username):
                raise serializers.ValidationError("Username already exists")

            if NeoUser.nodes.filter(email=email):
                raise serializers.ValidationError("Email already exists")

            if NeoUser.nodes.filter(phone=phone):
                raise serializers.ValidationError("Phone already exists")

            if NeoUser.nodes.filter(emp_id=emp_id) or NeoUser.nodes.filter(uid=id):
                raise serializers.ValidationError("Employee already exists")

            return data

        def create(self, validated_data):
            id = validated_data.get('uid')
            username = validated_data.get('username')
            email = validated_data.get('email')
            password = validated_data.get('password')
            emp_id = validated_data.get('emp_id')
            phone = validated_data.get('phone')

            # Create Django User if needed
            # user = NeoUser.nodes.create(username=username, email=email)

            # Create NeoUser
            neo_user = NeoUser(uid=id,username=username, email=email,password=password,emp_id=emp_id,phone=phone)
            neo_user.save()

            return validated_data

views.py

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import status
    from .models import NeoUser
    from neomodel import db
    from .serializers import Neo4jUserSerializer
    from rest_framework_simplejwt.tokens import RefreshToken
    from rest_framework.permissions import IsAuthenticated
    from rest_framework_simplejwt.views import TokenObtainPairView
    from rest_framework.decorators import  authentication_classes, permission_classes
    from datetime import datetime, timedelta


    class UserSignupView(APIView):
        def post(self, request):

          if request.method == 'POST':
            serializer = Neo4jUserSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response({"message": "User registered successfully", "status": 200}, status=status.HTTP_201_CREATED)
            else:
                return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)



    class UserLoginView(TokenObtainPairView):

        def post(self, request):
            email = request.data.get('email')
            password = request.data.get('password')

            if not email:
                return Response({'Error': "Please provide email", "status": 400}, status=status.HTTP_400_BAD_REQUEST)

            if not password:
                return Response({'Error': "Please provide password", "status": 400}, status=status.HTTP_400_BAD_REQUEST)
            try:
                neo_user = NeoUser.nodes.get(email=email)
            except NeoUser.DoesNotExist:
                return Response({"message": "User does not exist", "status": 404}, status=status.HTTP_404_NOT_FOUND)

            # Compare the provided password with the stored hash in your Neo4j model
            if neo_user.password == password and neo_user.email == email:
                refresh = RefreshToken.for_user(neo_user)
                access_token = refresh.for_user(neo_user)
                access_token_expiration = datetime.now()  + access_token.lifetime

                return Response({"message": "Login Successful",  "status": 200, 'access_token': str(access_token),
                'refresh_token': str(refresh),     'access_token_expires_at': access_token_expiration
            },
                status=status.HTTP_200_OK)
            else:
                return Response({"message": "Invalid credentials", "status": 401}, status=status.HTTP_401_UNAUTHORIZED)



    
   
    class UserListView(APIView):
        permission_classes = (IsAuthenticated,)


        def get(self, request):
            print("Authentication:", request.auth)
            users = NeoUser.nodes.all()
            if not users:
                return Response({"message": "No users found"}, status=status.HTTP_404_NOT_FOUND)
            serializer = Neo4jUserSerializer(users, many=True)
            return Response(serializer.data, status=status.HTTP_200_OK)

urls.py

     urlpatterns = [
        path('api/signup', views.UserSignupView.as_view(), name='user-signup'),
        path('api/userlist', views.UserListView.as_view(), name='user-list'),
        path('api/login', views.UserLoginView.as_view(), name='user-login'),
    ]

I am providing the Authorization with 'Bearer my token' format, but I still get this error. As you can see, I added the permission classes as needed. If I remove the authentication decorator, I am getting the following error:

    {
    "detail": "User not found",
    "code": "user_not_found"
}

But data exists in the db.

sample request format:

request type I am new to DRF and Neo4j; please pardon my noobness here.

  • Can you also provide examples of requests you send with headers? – Bohdan Aug 31 '23 at 16:22
  • @Bohdan , I edited my question and added the image. Please check. – Sanji Vinsmoke Aug 31 '23 at 17:35
  • can you please explain why you have added the `@authentication_classes([])` decorator for `UserListView` please. – Dishan Sachin Sep 01 '23 at 05:52
  • Certainly. Without that decorator, even though I was making the query like the one in the image, I got {details: you do not have permission to perform this action} error. – Sanji Vinsmoke Sep 01 '23 at 08:46
  • I don't see `JWT_AUTH_HEADER_PREFIX` among valid settings for `SIMPLE_JWT` (https://django-rest-framework-simplejwt.readthedocs.io/en/latest/settings.html), but I do see `AUTH_HEADER_TYPES`, try uncomenting it in your settings – Bohdan Sep 01 '23 at 10:33
  • @Bohdan , I commented that out to debug the issue. Even with that line I am getting the same error – Sanji Vinsmoke Sep 01 '23 at 14:24

2 Answers2

0

I think you need to remove the space

write

'JWT_AUTH_HEADER_PREFIX': 'Bearer ', # Remove space from here

Instead of

'JWT_AUTH_HEADER_PREFIX': 'Bearer',
0
    {
"detail": "User not found", 
"code": "user_not_found"}

With this error it can be solved by using a new token. So just login again and copy the new token and use that.

Also there is no need to use authentication_classes and permission_classes decorators. Those are for funtional based views.

With class based views you only have to set permission_classes = [whateverpermissions] and authentication_classes = [whateverauthentications] just as you've done now.

You don't even need to do this import: from rest_framework.decorators import authentication_classes, permission_classes unless you will use functional based views at some point in your code which may need them. For class based views, the attributes permission_classes and authentication_classes are already defined in the APIview class so you just have to set them.