Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inactive users returning the same error as wrong credentials #571

Closed
anoited007 opened this issue Jan 2, 2021 · 3 comments
Closed

Inactive users returning the same error as wrong credentials #571

anoited007 opened this issue Jan 2, 2021 · 3 comments

Comments

@anoited007
Copy link

I am using Django 2.2.14 with the configuration below for Djoser 2.1.0 but when trying to get JWT token for an inactive user, it returns the same error as using a wrong password which makes it tricky to differentiate. Am I missing something or that is the expected behaviour? I have look at the error messages defined constants.py but the error I expect isn't the one returned.
I get HTTP STATUS 401 with the detail below
{ "detail": "No active account found with the given credentials" }

My configuration is as follows:

    'LOGIN_FIELD': 'email', 
    'SEND_CONFIRMATION_EMAIL': True, 
    'PASSWORD_CHANGED_EMAIL_CONFIRMATION': True, 
    'USER_CREATE_PASSWORD_RETYPE': True, 
    'TOKEN_MODEL': None,  
    'SEND_ACTIVATION_EMAIL': True, 
    "LOGOUT_ON_PASSWORD_CHANGE": False,  
    "PASSWORD_RESET_SHOW_EMAIL_NOT_FOUND": True,  
    "USERNAME_RESET_SHOW_EMAIL_NOT_FOUND": True,  
    'PASSWORD_RESET_CONFIRM_URL': 'account/password/reset/confirm/{uid}/{token}',  
    'USERNAME_RESET_CONFIRM_URL': 'account/username/reset/  /{uid}/{token}', 
    'ACTIVATION_URL': 'account/activate/{uid}/{token}', 
}

I am also using `AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.AllowAllUsersModelBackend']`


@jk31
Copy link

jk31 commented Feb 12, 2021

Hello @anoited007,

have you found a solution yet?

I have the same problem. I plan to use the AllowAllUsersModelBackend and hoped that djoser supports this workflow.

While my custom login view allows an inactive user to login, djoser's token login view return 400.

Moreover, djoser's activation email does not work for inactive users who were logged in with my custom login view (and logged out before activation). Also, requests to base endpoints like /users/me/ are not possible with inactive users.

Update 1:
I see many related issues and will check them now.

Update 1.5:
The solutions in the related issues seem to be outdated.
Technically, one can create a custom is_active field in a CustomUser model and then use it for the desired purpose. But this requires custom activation emails and permissions.

@jhill-cmd
Copy link

Yes.

I succeed by doing my own view, own serializer. I'm not using the custom settings.

urls.py

    path('api/v1/auth/', include('djoser.urls')),
    path('api/v1/auth/jwt/create/token/', core.views.CustomTokenObtainPairView.as_view(),name='token_obtain_pair'),
    path('api/v1/auth/', include('djoser.urls.jwt')),

core/views.py

from rest_framework.response import Response
from rest_framework_simplejwt.views import TokenViewBase
from core.serializers import CustomTokenCreateSerializer

class CustomTokenObtainPairView(TokenViewBase):
    """
    Takes a set of user credentials and returns an access and refresh JSON web
    token pair to prove the authentication of those credentials.
    """
    serializer_class = CustomTokenCreateSerializer

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)

        serializer.is_valid(raise_exception=True)

        return Response(serializer.validated_data, status=status.HTTP_200_OK)

core.serializers.py

from django.contrib.auth import authenticate
import djoser.conf
from djoser.conf import settings
from django.contrib.auth.models import update_last_login
from rest_framework_simplejwt.serializers import TokenObtainSerializer
from rest_framework_simplejwt.settings import api_settings
from rest_framework_simplejwt.tokens import RefreshToken

class CustomTokenCreateSerializer(TokenObtainSerializer):
    password = serializers.CharField(required=False, style={"input_type": "password"})
    
    default_error_messages = {
        "invalid_credentials": settings.CONSTANTS.messages.INVALID_CREDENTIALS_ERROR,
        "inactive_account": settings.CONSTANTS.messages.INACTIVE_ACCOUNT_ERROR,
    }
    
    @classmethod
    def get_token(cls, user):
        return RefreshToken.for_user(user)
        
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.user = None
        self.fields[settings.LOGIN_FIELD] = serializers.CharField(required=False)
    
    def validate(self, attrs):
        password = attrs.get("password")
        params = {djoser.conf.settings.LOGIN_FIELD: attrs.get(djoser.conf.settings.LOGIN_FIELD)}
        self.user = authenticate(
            request=self.context.get("request"), **params, password=password
        )
        

        if not self.user:
            self.user = Account.objects.filter(**params).first()
            if self.user and not self.user.check_password(password):
                self.fail("invalid_credentials")
        if self.user and self.user.is_active:
            data = super().validate(attrs)
            refresh = self.get_token(self.user)
    
            data['refresh'] = str(refresh)
            data['access'] = str(refresh.access_token)
    
            if api_settings.UPDATE_LAST_LOGIN:
                update_last_login(None, self.user)
            
            return data

        if self.user and not self.user.is_active:
            self.fail("inactive_account")
        self.fail("invalid_credentials")

@anoited007
Copy link
Author

@jk31 Yeah I did find a solution that is quite similar to what @3skr0 has shown.

You can find the solution here. The issue has very little to do with Djoser directly as it uses Simple JWT package and that's where the error is set from.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants