rest_framework.authentication.BasicAuthentication
Fails to Work Properly When User.is_active
is False
#9249
Replies: 11 comments 8 replies
-
I faced same issue and fixed it by using custom authentication but I think it`s a bug |
Beta Was this translation helpful? Give feedback.
-
Looks like we should escalate this to an issue then. What do you say? |
Beta Was this translation helpful? Give feedback.
-
@sakthisanthosh010303 @MahmoudBayoumi19 Do you have a reference repo / implementation example that we can reproduce the issue? I have some questions about why JWT creation would require an authentication first, and the problem seems related to it; but without having something to debug it's lot harder to test that guess. |
Beta Was this translation helpful? Give feedback.
-
To answer your doubt regarding why we need HTTP basic authentication for a JWT creation endpoint, there are two reasons:
PS: I'll be sharing the code snippets after discussing with my enterprise's CTO soon. |
Beta Was this translation helpful? Give feedback.
-
I think there is confusion about the purpose/function of sending credentials as a payload vs accessing an endpoint with basic auth. I can try to help better when we have something we can debug. |
Beta Was this translation helpful? Give feedback.
-
@ulgens, we’re discussing only about RESTful APIs and the standard practices that comes with it in this discussion. DRF (Django REST Framework) is meant to deal only with designing robust RESTful API back-ends. The link you provided is built to work with Django and not DRF. |
Beta Was this translation helpful? Give feedback.
-
This is the endpoint method that handles requests to the @api_view(["GET"])
@authentication_classes([BasicAuthentication])
@permission_classes([])
def jwt_create_api_view(request):
access_token, refresh_token = (
JWT.encode(token_type="ACCESS", user=request.user),
JWT.encode(token_type="REFRESH", user=request.user)
)
return Response(
data={"access": access_token, "refresh": refresh_token},
status=HTTP_201_CREATED
) As seen, it uses the This behavior is potentially a bug. |
Beta Was this translation helpful? Give feedback.
-
Why don't we accept credentials to this endpoint? You see, to understand that, you need to understand the following elementary/basic things about Python and DRF.
|
Beta Was this translation helpful? Give feedback.
-
Sure. I totally agree to what you say and I totally believe that we're all skilled programmers with a busy 9-5 job schedule. I believe that the code snippet mentioned above along with the description is informative enough to be understood by a back-end developer. Also, when there is a standard documentation that explain how things work under the hood and it is meant to be known by someone as a basic requirement who wants to answer these questions, why to waste our precious time in our busy schedule explaining them? |
Beta Was this translation helpful? Give feedback.
-
@sakthisanthosh010303 @MahmoudBayoumi19 I tried to reproduce the issue and you are right if user is inactive we should get "User inactive or deleted." but we get "Invalid username/password." instead. I found what the problem is actually DRF's BasicAuthentication uses Django's authenticate function which requires AUTHENTICATION_BACKENDS which by default is ['django.conrib.auth.backends.ModelBackend'] and you will find in ModelBackend there is authenticate function which before returning user instance checks if user is active. So, to solve your problem you just have to override this class. Make a new file in user named backends.py from django.contrib.auth.backends import ModelBackend
class CustomBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
UserModel = get_user_model()
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
if username is None or password is None:
return
try:
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
UserModel().set_password(password)
else:
if user.check_password(password):
return user and in settings.py add line I think it will do the work. You will get "User inactive or deleted." in case of inactive user. |
Beta Was this translation helpful? Give feedback.
-
Thank you community for all the support provided. I'll close this discussion now. |
Beta Was this translation helpful? Give feedback.
-
I'm building an API back-end with DRF. The user sign-up process is as follows:
/api/auth/user
endpoint with a POST request to create a user (defaultUser
model used) resource.is_active
attribute toTrue
.The back-end completely works on JWT authentication.
BasicAuthentication
is used only for the JWT creation process. Hence, while making a request to/api/auth/jwt/create
, HTTP basic authentication must be performed by sending the base64 encoded string of<username>:<password>
asAuthorization
header.The front-end, after making a request to create a user, also makes another request with the same credentials to retrieve the JWT associated with the user. This is used for authentication for successive requests.
Now, the problem is that the
rest_framework.authentication.BasicAuthentication
class returns{"detail": "Invalid username/password."}
as response whileis_active
isFalse
(because the user hasn't verified his email ID with the link sent to the email account).Looking at the class declaration, it seems to be implemented fine and the expected behavior is that the class should return
{"detail": "User is not active."}
when the account is inactive.I couldn't figure out how to fix this issue and hence this discussion.
Beta Was this translation helpful? Give feedback.
All reactions