title: How to authorize django-rest-knox login path without 401 error?
body: <p>I'm trying to put an authentication api on my django app so I can start using a Vue+Node frontend.  I've read over <a href="http://james1345.github.io/django-rest-knox/" rel="nofollow noreferrer">the knox documentation</a>, but I don't seem to see anything suggesting what the issue is.  Any insight would be greatly appreciated.  My suspicious are below.</p>
  
  <p><strong>What I'm doing:</strong>  I was following <a href="http://v1k45.com/blog/modern-django-part-4-adding-authentication-to-react-spa-using-drf/" rel="nofollow noreferrer">this</a> tutorial which suggest using knox, which seems to have nice features.  When I try to curl to create a user, everything works as expected, but when I try to login (via curl), I get the following response (401) in my console:</p>
  
  <p>API Call</p>
  
  <pre><code>(venv) brads-mbp:ReportVisualization brads$ curl --request POST --url http://127.0.0.1:8000/backend/auth/register/ --header 'content-type: application/json' --data '{"username": "user3", "password": "hunter2"}'
  {"user":{"id":4,"username":"user3"},"token":"2b261fcdcbfff47cd8819accc2a3a08befa169c59982659a64012c680a69c3e8"}
  (venv) brads-mbp:ReportVisualization brads$ curl --request POST --url http://127.0.0.1:8000/backend/auth/login/ --header 'content-type: application/json' --data '{"username": "user3","password": "hunter2"}'
  {"detail":"Authentication credentials were not provided."}
  </code></pre>
  
  <p>Server Log</p>
  
  <pre><code>System check identified no issues (0 silenced).
  December 18, 2018 - 07:47:05
  Django version 2.1.3, using settings 'project.settings'
  Starting ASGI/Channels version 2.1.5 development server at http://127.0.0.1:8000/
  Quit the server with CONTROL-C.
  HTTP POST /backend/auth/register/ 200 [0.13, 127.0.0.1:61332]
  Unauthorized: /backend/auth/login/
  HTTP POST /backend/auth/login/ 401 [0.00, 127.0.0.1:61336]
  </code></pre>
  
  <p>The curl login response makes me think there is something wrong with my setup of knox or django's auth.  The server message seems to suggest an issue with the routing, so here is my urls:</p>
  
  <p>project/urls.py</p>
  
  <pre><code>from django.contrib import admin
  from django.urls import path, include
  from django.views.generic import RedirectView, TemplateView
  from django.contrib.auth import views as auth_views
  from .router import ROUTER
  
  urlpatterns = [
      # path('', auth_views.LoginView.as_view(template_name='registration/login.html'), name='login'),
      path('admin/', admin.site.urls),
      # path('favicon.ico', RedirectView.as_view(url='/static/project/images/favicon.ico')),
      # path('background.jpg', RedirectView.as_view(url='/static/project/images/background.jpg')),
      # path('logout/', auth_views.LogoutView.as_view(template_name='users/logout.html'), name='logout'),
      # path('home/', TemplateView.as_view(template_name="index.html")),
      path('backend/', include('backend.urls')),
      path('api/', include(ROUTER.urls))
  ] 
  </code></pre>
  
  <p>backend/urls.py</p>
  
  <pre><code>from django.urls import path, include
  
  from . import views, authorization_views, dash_views
  
  urlpatterns = [
       # path('filter/', views.DataFilter.as_view(), name='filter'),
       # path('entry/', views.CreateDataEntryBlock.as_view(), name='entry'),
       # path('home/', views.home_view, name='home'),
  #     path('home/', views.index, name='index'),
  #     path('public/', views.public),
  #     path('private/', views.private),
       path('auth', include('knox.urls')),
       path('auth/register/', authorization_views.RegistrationAPI.as_view()),
       path('auth/login/', authorization_views.LoginAPI.as_view()),
  ]
  </code></pre>
  
  <p>authorization_views.py</p>
  
  <pre><code>from rest_framework import viewsets, permissions, generics
  from rest_framework.response import Response
  
  from knox.models import AuthToken
  
  # from Domino.backend.models import User
  from .serializers import CreateUserSerializer, UserSerializer, LoginUserSerializer
  
  
  class RegistrationAPI(generics.GenericAPIView):
      serializer_class = CreateUserSerializer
  
      def post(self, request, *args, **kwargs):
          serializer = self.get_serializer(data=request.data)
          serializer.is_valid(raise_exception=True)
          user = serializer.save()
          return Response({
              "user": UserSerializer(user, context=self.get_serializer_context()).data,
              "token": AuthToken.objects.create(user)
          })
  
  
  class LoginAPI(generics.GenericAPIView):
      serializer_class = LoginUserSerializer
  
      def post(self, request, *args, **kwargs):
          import pdb; pdb.set_trace()
          print("LoginAPI - post")
          serializer = self.get_serializer(data=request.data)
          serializer.is_valid(raise_exception=True)
          user = serializer.validated_data
          return Response({
              "user": UserSerializer(user, context=self.get_serializer_context()).data,
              "token": AuthToken.objects.create(user)
          })
  </code></pre>
  
  <p>backend/serializers.py</p>
  
  <pre><code>from datetime import datetime, timedelta
  from rest_framework import serializers
  from django.contrib.auth.models import User
  from django.contrib.auth import authenticate
  
  
  class CreateUserSerializer(serializers.ModelSerializer):
      class Meta:
          model = User
          fields = ('id', 'username', 'password')
          extra_kwargs = {'password': {'write_only': True}}
  
      def create(self, validated_data):
          user = User.objects.create_user(validated_data['username'],
                                          None,
                                          validated_data['password'])
          return user
  
  
  class LoginUserSerializer(serializers.Serializer):
      username = serializers.CharField()
      password = serializers.CharField()
  
      def validate(self, data):
          import pdb; pdb.set_trace()
          print("LoginAPI - post")
          user = authenticate(**data)
          if user and user.is_active:
              return user
          raise serializers.ValidationError("Unable to log in with provided credentials.")
  
  
  
  class UserSerializer(serializers.ModelSerializer):
      class Meta:
          model = User
          fields = ('id', 'username')
  </code></pre>
  
  <p>project/settings.py</p>
  
  <pre><code>import os
  
  # import json
  # from six.moves.urllib import request
  # from cryptography.x509 import load_pem_x509_certificate
  # from cryptography.hazmat.backends import default_backend
  
  BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  
  SECRET_KEY = '...'
  
  DEBUG = True
  
  ALLOWED_HOSTS = ['*']
  
  
  # Application definition
  
  INSTALLED_APPS = [
      'backend.apps.BackendConfig',
      'django.contrib.admin',
      'django.contrib.auth',
      'django.contrib.contenttypes',
      'django.contrib.sessions',
      'django.contrib.messages',  
      'django.contrib.staticfiles',
      'channels',
      'widget_tweaks',
      'django_filters',
      'crispy_forms',
      'rest_framework',
      'knox',
      # 'rest_framework_jwt'
      'corsheaders',
  ]
  
  # App settings
  CORS_ORIGIN_ALLOW_ALL = True
  
  MIDDLEWARE = [
      'corsheaders.middleware.CorsMiddleware',
      'django.middleware.security.SecurityMiddleware',
      'django.contrib.sessions.middleware.SessionMiddleware',
      'django.middleware.common.CommonMiddleware',
      'django.middleware.csrf.CsrfViewMiddleware',
      'django.contrib.auth.middleware.AuthenticationMiddleware',
      'django.contrib.messages.middleware.MessageMiddleware',
      'django.middleware.clickjacking.XFrameOptionsMiddleware',
  ]
  
  ROOT_URLCONF = 'project.urls'
  
  TEMPLATES = [
      {
          'BACKEND': 'django.template.backends.django.DjangoTemplates',
          'DIRS': [os.path.join(BASE_DIR, 'project/templates'),
                   os.path.join(BASE_DIR, 'backend/templates'),
                  ],
          'APP_DIRS': True,
          'OPTIONS': {
              'context_processors': [
                  'django.template.context_processors.debug',
                  'django.template.context_processors.request',
                  'django.contrib.auth.context_processors.auth',
                  'django.contrib.messages.context_processors.messages',
              ],
          },
      },
  ]
  
  WSGI_APPLICATION = 'project.wsgi.application'
  ASGI_APPLICATION = "project.routing.application"
  
  # Database
  # https://docs.djangoproject.com/en/2.1/ref/settings/#databases
  
  DATABASES = {
      'default': {
          'ENGINE': 'djongo',
          'NAME': 'test',
      }
  }
  
  
  # Password validation
  # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
  
  AUTH_PASSWORD_VALIDATORS = [
      {
          'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
      },
      {
          'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
      },
      {
          'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
      },
      {
          'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
      },
  ]
  
  
  # Internationalization
  # https://docs.djangoproject.com/en/2.1/topics/i18n/
  
  LANGUAGE_CODE = 'en-us'
  TIME_ZONE = 'UTC'
  USE_I18N = True
  USE_L10N = True
  USE_TZ = True
  
  
  # Static files (CSS, JavaScript, Images)
  # https://docs.djangoproject.com/en/2.1/howto/static-files/
  
  LOGIN_URL = ''
  LOGIN_REDIRECT_URL = 'backend/home/'
  
  STATIC_URL = '/static/'
  # STATIC_ROOT = os.path.join(BASE_DIR, '../static/')
  
  STATICFILES_DIRS = [
      os.path.join(BASE_DIR, "project/static"),
      os.path.join(BASE_DIR, "backend/static"),
  ]
  
  REST_FRAMEWORK = {
      'DEFAULT_AUTHENTICATION_CLASSES': ('knox.auth.TokenAuthentication',),
  }
  </code></pre>
  
  <p>Environment</p>
  
  <pre><code>(venv) brads-mbp:ReportVisualization brads$ pip list                                   
  
  brads$
  Package                 Version
  ----------------------- ----------
  asgiref                 2.3.2
  asn1crypto              0.24.0
  astroid                 2.1.0
  async-timeout           3.0.1
  atomicwrites            1.2.1
  attrs                   18.2.0
  autobahn                18.11.2
  Automat                 0.7.0
  certifi                 2018.11.29
  cffi                    1.11.5
  channels                2.1.5
  chardet                 3.0.4
  constantly              15.1.0
  cryptography            2.4.2
  daphne                  2.2.3
  dataclasses             0.6
  Django                  2.1.3
  django-cors-headers     2.4.0
  django-crispy-forms     1.7.2
  django-filter           2.0.0
  django-rest-knox        3.4.0
  django-widget-tweaks    1.4.3
  djangorestframework     3.9.0
  djangorestframework-jwt 1.11.0
  djongo                  1.2.30
  ecdsa                   0.13
  future                  0.17.1
  hyperlink               18.0.0
  idna                    2.7
  incremental             17.5.0
  isort                   4.3.4
  lazy-object-proxy       1.3.1
  mccabe                  0.6.1
  more-itertools          4.3.0
  numpy                   1.15.4
  Pillow                  5.3.0
  pip                     18.1
  pluggy                  0.8.0
  py                      1.7.0
  pyasn1                  0.4.4
  pyasn1-modules          0.2.2
  pycparser               2.19
  PyHamcrest              1.9.0
  PyJWT                   1.7.1
  pylint                  2.2.2
  pylint-django           2.0.4
  pylint-plugin-utils     0.4
  pymongo                 3.7.2
  pyOpenSSL               18.0.0
  pytest                  4.0.1
  python-jose             3.0.1
  pytz                    2018.7
  requests                2.20.1
  rope                    0.11.0
  rsa                     4.0
  scipy                   1.1.0
  service-identity        18.1.0
  setuptools              40.6.2
  six                     1.11.0
  sqlparse                0.2.4
  Twisted                 18.9.0
  txaio                   18.8.1
  typed-ast               1.1.0
  urllib3                 1.24.1
  wheel                   0.32.3
  wrapt                   1.10.11
  zope.interface          4.6.0
  </code></pre>
  
  <p>I feel like I have to have something simple that is wrong with a setting, but I can't seem to figure out what it is.  I saw some posts that claimed an issue with "Apache and mod_wsgi" which isn't an issue for me as I am running local atm, however I'm running channels so I wonder if that has any impact, or maybe it has to do with the auth password validators in settings?</p>