pythondjangodjango-rest-frameworkdjango-viewshttp-token-authentication

Django REST framework TokenAuthentication returns anonymous user


How do I properly implement DRF TokenAuthentication without the request object returning an anonymous user when I try to log in?

according to the docs, when authenticated, the TokenAuthentication object provides the request.user which is the Django user instance and the request.auth which is the token instance. But even after authentication, request.user returns anonymouse user.

What could I be doing wrong?

Client request:

//function to get token
export default function axiosConfig() {
    // request header
    const headers = {
        "Content-Type": "application/json"
    }

    // Get token from local storage. Token is stored when user registers.
    const token = localStorage.getItem("token");

    if (token) headers["Authorisation"] = `Token ${token}`;

    return headers;

}

Redux action

import axiosConfig from "../../utils/axiosConfig";

const config = axiosConfig

export const login = (email, password) => (dispatch, getState) => {

    const body = { email, password };

    // Change to absoulte path when deploying to production
    axios
        .post("http://localhost:8000/api/auth/login", body, config())
        .then((res) => {
            dispatch({
                type: SIGN_IN_SUCCESFUL,
                payload: res.data,
            });
            console.log(res);
        })
        .catch((err) => {
            dispatch({
                type: SIGN_IN_FAIL,
                payload: err.response,
            });
            console.log(err.response.data, err.response.status);
        });
};

Django

url:

from django.urls import path
from authentication.views import RegisterationView
from authentication.views import LoginView
from authentication.views import LogoutView

urlpatterns = [
    path("auth/register", RegisterationView.as_view()),
    path("auth/login", LoginView.as_view()),
    path("auth/logout/<int:id>", LogoutView.as_view()),
]

Serializer:

The LoginResponseSerializer is used to provide response data to the client

class LoginSerializer(serializers.Serializer):
    """Login serializer"""

    username = serializers.CharField()
    password = serializers.CharField(required=True)


class LoginResponseSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = [
            "id",
            "username",
            "first_name",
            "last_name",
            "email",
            "is_active",
            "is_staff",
        ]

        read_only_fields = ["id", "is_active", "is_staff"]

View:

class LoginView(APIView):
    """Login View"""

    permision_classs = [permissions.AllowAny]

    def post(self, request):
        serializer = LoginSerializer(data=request.data)

        if serializer.is_valid():
            print(serializer.data) # Data is present

            user = authenticate(request, **serializer.data) # Valid credentials. User object is returned.
            response_serializer = LoginResponseSerializer(user)

            if user is not None and login(request, user):
                print(request.user) # User is anonymous

                token, created_token = Token.objects.get_or_create(user_id=user.id)

                if isinstance(created_token, Token):
                    token = created_token

                return Response(
                    {
                        "user": response_serializer.data,
                        "status": {
                            "message": "user authenticated",
                            "code": status.HTTP_200_OK,
                        },
                        "token": token.key,
                    }
                )

            raise serializers.ValidationError(
                "Invalid Username or Password. Please try again"
            )

        return Response(
            {"error": serializer.errors, "status": status.HTTP_403_FORBIDDEN}
        )

Solution

  • Since you are using Token authentication, your users will be authenticated with the token in the header, for each request.

    Django login() is useful in case of SessionAuthentication. Where user is stored in the session object in django, identified by the session cookie.

    In your view, you don't have to call the login method. Just return the token and whatever extra information you want. And make sure you are sending this token in every request to authenticate this user.

    EDIT: And the clarification about the request.user in the documentation of DRF, is about accessing the authenticated user in the other view where you provide token in headers.