Login Issue with local and deployed it behaves differents

Hello team,

I have a scenario where there is one superuser who is creating multiple users. I have developed a login API that issues an access token.

Here’s the situation:

When I hit the login API from my local machine (which is connected to the dev-deployed database), everything works fine, and I receive the access token.
However, after deploying the API to the development environment, the behavior changes. When I call the API for the first time, it works as expected, and I receive the token. But when I try to hit the same API again, it returns an "invalid credentials" error.
Interestingly, if I go to the Django admin page and create the user directly through the deployed environment, I can log in with the API successfully every time with that same user.

So, I’m unsure what might be causing the issue when calling the login API from my local machine.

Could anyone help me understand what might be going wrong in this situation?

Welcome @nilankh !

You mention that if you create the users using the admin, it works, but users created with your process don’t work. Am I understanding this correctly?

If so, please show your code where you are creating your users.

1 Like

Thankyou for your reply @KenWhitesell

My localhost is connected to the development environment database.

I went to the Django admin on my localhost and created a few users, and they were created successfully.

When I use the same user ID and password to log in through the localhost API, it works fine.

However, when I try to use the same user through the development environment API, it works the first time and gives me an access token. But after that, it says "invalid credentials

if user_obj.check_password(kwargs[“validated_data”][“password”]):

i am using check_password to validate password is correct or not

For us to try to help you, you need to provide the complete code involved. Show the complete models and views being used. Don’t just try to describe your environment, show the details.

class UserManager(BaseUserManager):
    def create_user(
        self,
        user_name,
        email,
        first_name,
        last_name,
        emp_code,
        phone_number,
        role_tag,
        branch_id,
        branch_name,
        user_type,
        password=None,
        created_by=None,
    ):
        if not email:
            raise ValueError("Users must have an email address")

        user = self.model(
            email=self.normalize_email(email),
            user_name=user_name,
            first_name=first_name,
            last_name=last_name,
            emp_code=emp_code,
            phone_number=phone_number,
            role_tag=role_tag,
            branch_id=branch_id,
            branch_name=branch_name,
            user_type=user_type,
            created_by=created_by,
        )

        if password:
            user.set_password(password)
        else:
            raise ValueError("Password must be provided")

        # Save user to database
        user.save(using=self._db)
        return user

    def create_superuser(
        self,
        user_name,
        email,
        first_name,
        last_name,
        emp_code,
        phone_number,
        role_tag,
        branch_id,
        branch_name,
        user_type,
        password=None,
        created_by=None,
    ):
        user = self.create_user(
            user_name=user_name,
            email=email,
            first_name=first_name,
            last_name=last_name,
            emp_code=emp_code,
            phone_number=phone_number,
            role_tag=role_tag,
            branch_id=branch_id,
            branch_name=branch_name,
            user_type=user_type,
            password=password,
            created_by=created_by,
        )
        user.is_admin = True
        user.is_active = True
        user.save(using=self._db)
        return user


class User(AbstractBaseUser):
    USER_TYPE_CHOICES = [
        ("maker", "Maker"),
        ("checker", "Checker"),
        ("mis_view", "MIS View"),
        ("admin", "Admin"),
    ]

    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    user_name = models.CharField(max_length=50, unique=True)
    emp_code = models.CharField(max_length=50)
    email = models.EmailField(unique=True)
    phone_number = models.CharField(max_length=15)
    role_tag = models.CharField(max_length=50)
    is_active = models.BooleanField(default=True)
    branch_id = models.IntegerField()
    branch_name = models.CharField(max_length=50)
    deleted_by_admin_id = models.IntegerField(null=True, blank=True)
    user_type = models.CharField(max_length=20, choices=USER_TYPE_CHOICES)
    created_by = models.IntegerField()
    updated_by = models.IntegerField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    is_admin = models.BooleanField(default=False)

    objects = UserManager()

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = [
        "user_name",
        "first_name",
        "last_name",
        "emp_code",
        "phone_number",
        "role_tag",
        "branch_id",
        "branch_name",
        "user_type",
    ]

    def __str__(self):
        return f"{self.user_name} ({self.first_name} {self.last_name})"

    @property
    def is_staff(self):
        return self.is_admin

    def soft_delete(self, admin_user):
        """Mark the user as inactive and record the admin's ID."""
        if admin_user.is_admin:
            self.is_active = False
            self.deleted_by_admin_id = admin_user.id
            self.save()

    def delete(self, *args, **kwargs):
        """Override delete to prevent actual deletion."""
        raise Exception("Deletion is not allowed. Use soft_delete instead.")

    def save(self, *args, **kwargs):
        if self.password:
            self.set_password(self.password)
        super(User, self).save(*args, **kwargs)

this is my model for User

to login

 def login(self, *args, **kwargs):

        user_obj = self.user_dao.get_user(
            email=kwargs["validated_data"]["email"], is_active=True
        )
        if not user_obj:
            return self.request_response_handler.return_response(
                {
                    "detail": f"{kwargs['validated_data']['email']} this email not found!"
                },
                status_code=status.HTTP_400_BAD_REQUEST,
            )
        if user_obj.check_password(kwargs["validated_data"]["password"]):

            token_obj = self.token_dao.get_token(user_id=user_obj)
            if not token_obj:
                token_obj = self.token_dao.create(user_id=user_obj)
                token_obj.set_access_key()
                token_obj.set_refresh_key()
                return self.request_response_handler.return_response(
                    {"access_token": token_obj.access_key},
                    status_code=status.HTTP_200_OK,
                )

            elif token_obj.is_token_expired():
                token_obj.set_access_key()
                token_obj.set_refresh_key()
                response = self.request_response_handler.return_response(
                    {"access_token": token_obj.access_key},
                    status_code=status.HTTP_200_OK,
                )
                # Set the refresh token as HttpOnly cookie
                response.set_cookie(
                    key="refresh_token",
                    value=token_obj.refresh_key,
                    httponly=True,  # HttpOnly cookie for security
                    secure=settings.SECURE_SSL_REDIRECT,  # Use secure cookies in production
                    samesite="Lax",  # Prevents CSRF, use 'Strict' if compatible
                    max_age=14
                    * 24
                    * 60
                    * 60,  # Set to your refresh token's expiration time
                )
                return response
            return self.request_response_handler.return_response(
                {"access_token": token_obj.access_key}, status_code=status.HTTP_200_OK
            )
        else:
            return self.request_response_handler.return_response(
                {"error": "Invalid credentials"},
                status_code=status.HTTP_401_UNAUTHORIZED,
            )

Side Note: When posting code here, enclose the code between lines of three
backtick - ` characters. This means you’ll have a line of ```, then your code,
then another line of ```. This forces the forum software to keep your code
properly formatted. (I have taken the liberty of correcting your post.
Please remember to do this in the future.)

Okay @KenWhitesell

Thankyou for correcting, I will remember it

There’s still a lot missing here.

What class is this login function in?

What are these user_dao and token_dao objects?

This is my view Class

class LoginAPIView(APIView):
    """
    API endpoint for user login
    """

    def post(self, request):
        self.request_response_handler = RequestResponseHandler(
            serializer_obj=schema.LoginSerializer(data=request.data)
        )

        validated_data = self.request_response_handler.validate_serializer()
        self.user_dao = UserDao()
        self.token_dao = TokensDao()
        self.api_controller = CommonController(
            user_dao=self.user_dao,
            token_dao=self.token_dao,
            request_response_handler=self.request_response_handler,
        )

        return self.api_controller.login(request, validated_data=validated_data)

from users.models import User

This is UserDao Class where i write the query

class UserDao:
    def get_user(self, *args, **kwargs):
        try:
            return User.objects.get(*args, **kwargs)
        except User.DoesNotExist:
            return False

and this the token_dao class

from users.models import Tokens

class TokensDao:
    def get_token(self, *args, **kwargs):
        try:
            return Tokens.objects.get(*args, **kwargs)
        except Tokens.DoesNotExist:
            return False

    def create(self, **kwargs):
        return Tokens.objects.create(**kwargs)

@KenWhitesell

any thought which you would like to share?

Sure. You’re using DRF, and I hope someone who knows more about DRF than I do jumps into this thread.

From a Django perspective, my only thought would be to compare every field between an account that works and an account that doesn’t, to see if there’s a difference.

You could add some print statements to verify that the data you’re seeing at this point is what it’s supposed to be, or run it in the debugger to step through it and examine it. (You’ll also want to verify that what you’re seeing submitted to the API is what you’re expecting it to be.)

I might also use the Django shell to manually run the code that’s failing to see where it’s not working.

There are some people knowledgeable about DRF who do try to help here. But this forum isn’t an official support channel for DRF. You might get better or faster answers if you raise your question in one of the areas listed in Support.

thankyou for your response @KenWhitesell

i tried to do the same thing.

I have just one question.

I am living in Spain, but the server’s timezone is different for the deployed URL. The users are from India. Will the timezone difference affect the hashing of the password? I know this might be a silly question it just crossed my mind