K
K
Kit Scribe2022-03-10 16:13:48
Django
Kit Scribe, 2022-03-10 16:13:48

What is the problem with CSRF in django/drf?

No matter how many times I create a project, I keep running into an error. "CSRF verification failed. Request aborted."

I turned off the middleware, I used the decorator @csrf_exempt, I tried to rewrite everything to DRF and back several times. In the chats, I was advised different solutions, but nothing has helped so far, and every POST request crashes with this error.

There is a path in the application /api/v1/token/- a view is assigned to it, processing POST, in which, if the credits match, a token is generated and assigned to the User. The view is decorated with @csrf_exempt, but still returns a CSRF error

. And if a GET request is made on this view, a 405 status should be returned, but the response to the request is 302, which can be seen in the logs:

"GET /api/v1/token/ HTTP/1.1" 302 0

And here are the POST logs

Forbidden (CSRF cookie not set.): /api/v1/token/
"POST /api/v1/token/ HTTP/1.1" 403 2870


Here is the list of middleware

MIDDLEWARE = [
    # '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',
]


Here are the urls

urlpatterns = [
    path('token', views.token_auth),
]



And view

@csrf_exempt
def token_auth(request):
    """
    Запрос нового токена
    """
    if request.method == "POST":
        data = json.loads(request.body)

        # если не переданы авторизацонные данные
        if 'username' not in data or 'password' not in data:
            return HttpResponse(json.dumps({"error": "No credentials provided!"}), status=500)

        user = authenticate(username=data["username"], password=data["password"])
        # если авторизационные данные не верны
        if not user:
            return HttpResponse(json.dumps({"error": "No user with such credentials found!"}), status=400)

        # если успешно
        token = Token.generate_token(user)
        return HttpResponse(json.dumps({"token": token}), status=200)
    return HttpResponse(json.dumps({"error": "Not allowed method! Use POST instead!"}), status=405)


It turns out that some other view is being processed? What could be the problem? How is this possible? Now I'm trying to solve the problem using only django. Does anyone have any ideas?

UPD
There is one application in the project: token_auth

There is
decorators.py

from token_auth.models import Token
from django.http import HttpResponse
import json


def check_token(fn):
    # декоратор проверки валиден ли токен
    def wrapper_func(request, *args, **kwargs):
        token = request.headers.get("Authentication", None)

        # если токен не передан в хэдере
        if not token:
            return HttpResponse(json.dumps({"error": "No token given!"}), status=500)
        token = token.replace("Token ", "")

        # если токен не валиден
        token = Token.objects.get(token=token)
        if not token:
            return HttpResponse(json.dumps({"error": "Token is invalid!"}), status=400)

        # если успешно
        return fn(request, *args, **kwargs)
    return wrapper_func


models.py
from django.contrib.auth.models import User
from django.db import models
import random
import string


class Token(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    token = models.CharField(max_length=128)

    @staticmethod
    def generate_token(user: User):
        ...
        return token


urls.py
from django.urls import path
from token_auth import views

urlpatterns = [
    path('token/', views.token_auth),
]


views.py
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth import authenticate
from django.http import HttpResponse
from token_auth.models import Token
import json


@csrf_exempt
def token_auth(request):
    """
    Запрос нового токена
    """
    if request.method == "POST":
        data = json.loads(request.body)

        # если не переданы авторизацонные данные
        if 'username' not in data or 'password' not in data:
            return HttpResponse(json.dumps({"error": "No credentials provided!"}), status=500)

        user = authenticate(username=data["username"], password=data["password"])
        # если авторизационные данные не верны
        if not user:
            return HttpResponse(json.dumps({"error": "No user with such credentials found!"}), status=400)

        # если успешно
        token = Token.generate_token(user)
        return HttpResponse(json.dumps({"token": token}), status=200)
    return HttpResponse(json.dumps({"error": "Not allowed method! Use POST instead!"}), status=405)


root urls
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from core import settings

urlpatterns = [
    path('', admin.site.urls),
    path('api/v1/', include('token_auth.urls')),
]

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


root settings.py
"""
Django settings for core project.

Generated by 'django-admin startproject' using Django 3.2.12.

For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""
import os
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-b3z*0+wb+ba#%ksak3aamrpf6#-x_x&q19$2$+%a-i906kldbg'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'token_auth',
]

MIDDLEWARE = [
    '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 = 'core.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        '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 = 'core.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/3.2/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/3.2/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/3.2/howto/static-files/

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static")
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, "media")


# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': os.path.join(BASE_DIR, 'worker.log'),
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question