Reputation: 69
My nextjs application integrated with django has an authentication system based on csrftoken and sessionid. Once the nextjs application runs, it makes a request to the backend for the csrf route, which automatically sets a cookie to the destination of the request. This works normally running locally. But when running nextjs in docker in the production environment you can see that the cookie is set in the response headers, but it does not actually create a cookie. I had seted CSRF_COOKIE_SAMESITE = None and SESSION_COOKIE_SAMESITE = None But still not setting the cookie, even in the headers showing the set cookie
XHRGET
https://api.viajahturismo.com.br/api/v1/csrf/
[HTTP/2 204 23ms]
csrftoken
expires "2025-03-09T00:38:39.000Z"
path "/"
samesite "None"
value "8UayLGpyXUF0cc2AlM5zqBd4kdOHSfXf"
csrftoken "8UayLGpyXUF0cc2AlM5zqBd4kdOHSfXf"
My django setting:
"""
Django settings for myproject project.
Generated by 'django-admin startproject' using Django 4.2.1.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.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/4.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.getenv('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# ALLOWED_HOSTS = ['*']
# CSRF_TRUSTED_ORIGINS = ['*']
ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS').split(',') if os.getenv('ALLOWED_HOSTS') else []
CSRF_TRUSTED_ORIGINS = os.getenv('CSRF_TRUSTED_ORIGINS').split(',')
# Application definition
INSTALLED_APPS = [
'corsheaders',
'viajah.apps.ViajahConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'drf_yasg',
]
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',
"whitenoise.middleware.WhiteNoiseMiddleware",
]
ROOT_URLCONF = 'myproject.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 = 'myproject.wsgi.application'
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': os.environ.get("DB_NAME"),
'USER': os.environ.get("DB_USER"),
'PASSWORD': os.environ.get("DB_PASSWORD"),
'HOST': os.environ.get("DB_HOST"),
'PORT': os.environ.get("DB_PORT"),
}
}
# Password validation
# https://docs.djangoproject.com/en/4.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/4.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
CELERY_BROKER_URL = 'redis://redis:6379/0'
CELERY_RESULT_BACKEND = 'redis://redis:6379/0'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
},
'celery': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
},
},
}
# ---------------- Local Settings ---------------------------------------
# Put your local settings in mydjango directory to override this settings
# File name should be local_settings.py
try:
from .local_settings import *
except ImportError:
print('No Local Settings Found')
# ---------------- End Local Settings ------------------------------------
# # Default primary key field type
# # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": 'rest_framework.pagination.PageNumberPagination',
"PAGE_SIZE": 10,
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
],
}
AUTH_USER_MODEL = 'viajah.Usuario'
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
CSRF_COOKIE_PATH = '/'
SESSION_COOKIE_PATH = '/'
CSRF_COOKIE_SAMESITE = None
SESSION_COOKIE_SAMESITE = None
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
CORS_ALLOWED_ORIGINS = os.getenv('CORS_ALLOWED_ORIGINS').split(',')
CORS_ORIGIN_WHITELIST = os.getenv('CORS_ORIGIN_WHITELIST').split(',')
# CSRF_COOKIE_DOMAIN = os.getenv('CSRF_COOKIE_DOMAIN')
CORS_EXPOSE_HEADERS = ["Content-Type", "X-CSRFToken"]
CORS_ALLOW_CREDENTIALS = True
EMAIL_BACKEND = os.getenv('EMAIL_BACKEND')
EMAIL_HOST = os.getenv('EMAIL_HOST')
EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD')
EMAIL_PORT = os.getenv('EMAIL_PORT')
EMAIL_USE_TLS = True
EMAIL_USE_SSL = False
My nginx settings in production:
upstream lito_upstream {
# ip_hash;
server viajah-api:8000;
server front_viajah:3000;
}
server {
location / {
proxy_pass http://viajah-api:8000/;
}
listen 8000;
server_name viajah-api;
}
#server
server {
#Defines the port on which the server will listen for requests.
include /etc/nginx/mime.types;
location /media/ {
autoindex on;
alias /app/media/;
}
location / {
proxy_set_header Host $host;
proxy_pass http://front_viajah:3000;
}
listen 3000;
server_name front_viajah;
My view to get csrf
class get_csrf(APIView):
authentication_classes = [SessionAuthentication]
permission_classes = [AllowAny]
@method_decorator(ensure_csrf_cookie)
def get(self, request, *args, **kwargs) -> Response:
return Response(status=status.HTTP_204_NO_CONTENT)
Ps: In localhost and running locally with docker it works really fine, the cookie is setted ok, and I can do the other requests that depends of csrfToken and sessionID. But in my production in a VPS server with Docker pulling the images from docker hub pushed by gh actions dont't work. My domains are mydomain.com.br and api.mydomain.com.br
Please if someone can help me, I don't have any idea of what is happening
Upvotes: 0
Views: 233
Reputation: 69
The problem was because I had to add CSRF_COOKIE_DOMAIN
and SESSION_COOKIE_DOMAIN
to acept the domain, you have to add the domain whithout https://
. If you not add these two options django will set host only for the cookies, which will only allow the cookies for api domain.
Upvotes: 0