Django not setting cookies after deployment

I have created a vue and DRF application, which is working fine locally, it sets access, refresh and csrf token in cookies on login and so all of the isAuthenticated routes work fine.

But i handed it over to the person who's meant to deploy this on vps and now it doesn't set cookie on login. I'm able to login, but since no cookie is set, refreshing it logs me out and all routes give 401 error.

These are my django settings, removing secret_key here:


import os
from pathlib import Path
from datetime import timedelta

# 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: 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',
    'rest_framework',
    'rest_framework_api_key',
    'rest_framework_simplejwt.token_blacklist',
    'corsheaders',
    'django_filters',
    'api'
]

AUTH_USER_MODEL = 'api.User'

AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.AllowAllUsersModelBackend',
)

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

CORS_ALLOW_ALL_ORIGINS = True

CORS_ALLOW_HEADERS = [
    "accept",
    "accept-encoding",
    "authorization",
    "content-type",
    "dnt",
    "origin",
    "user-agent",
    "x-csrftoken",
    "x-requested-with",
    "fxbNh4",
]
# CORS_ALLOWED_ORIGINS = [
#     "http://localhost:8080"
# ]
# CORS_ALLOWED_ORIGINS = [
#     'http://34.67.29.99',
# ]

CORS_ALLOW_CREDENTIALS = True

ROOT_URLCONF = 'InstaBackend.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR)],
        '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 = 'InstaBackend.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 = 'Asia/Karachi'

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_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
    ),
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 12,
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated'
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'api.authenticate.CustomAuthentication',
    ),
    'DATETIME_FORMAT': "%Y-%m-%d %H:%M:%S",
}

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(days=1),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
    'ROTATE_REFRESH_TOKENS': False,
    'BLACKLIST_AFTER_ROTATION': True,
    'UPDATE_LAST_LOGIN': False,
    'ALGORITHM': 'HS256',
    'SIGNING_KEY': SECRET_KEY,
    'VERIFYING_KEY': None,
    'AUDIENCE': None,
    'ISSUER': None,
    'AUTH_HEADER_TYPES': ('Bearer',),
    'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',
    'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
    'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
    'TOKEN_TYPE_CLAIM': 'token_type',
    'JTI_CLAIM': 'jti',
    'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
    'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
    'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
    'AUTH_ACCESS_COOKIE': 'access_token',  # Cookie name. Enables cookies if value is set.
    'AUTH_REFRESH_COOKIE': 'refresh_token',  # Cookie name. Enables cookies if value is set.
    'AUTH_CSRF_ACCESS_COOKIE': 'csrf_access_token',  # Cookie name. Enables cookies if value is set.
    'AUTH_CSRF_REFRESH_COOKIE': 'csrf_refresh_token',  # Cookie name. Enables cookies if value is set.
    'AUTH_COOKIE_DOMAIN': None,  # A string like "example.com", or None for standard domain cookie.
    'AUTH_COOKIE_SECURE': False,  # Whether the auth cookies should be secure (https:// only).
    'AUTH_COOKIE_HTTP_ONLY': False,  # Http only cookie flag.It's not fetch by javascript.
    'AUTH_COOKIE_PATH': '/',  # The path of the auth cookie.
    'AUTH_COOKIE_SAMESITE': None,  # Whether to set the flag restricting cookie leaks on cross-site requests.
                                    # This can be 'Lax', 'Strict', or None to disable the flag.
}

# CSRF_COOKIE_DOMAIN = '34.67.29.99'
# # CSRF_COOKIE_SECURE = True
# # USE_X_FORWARDED_HOST = True
# # SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# CSRF_TRUSTED_ORIGINS = ['http://34.67.29.99/','http://34.67.29.99',]
# CSRF_COOKIE_SAMESITE = 'Lax'
# CSRF_COOKIE_HTTPONLY = False

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


DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

This is my login_view:

from django.middleware import csrf
from rest_framework.views import APIView
from rest_framework.response import Response
from django.contrib.auth import authenticate
from rest_framework import status
from api.util import (set_access_cookies, set_refresh_cookies, get_tokens_for_user, combine_role_permissions,
                      extract_patient_role)
from api.serializers import UserSerializer, RoleSerializer
from django.utils import timezone
from api.models import User
from rest_framework_api_key.permissions import HasAPIKey
from rest_framework.permissions import AllowAny


class LoginView(APIView):
    authentication_classes = ()
    permission_classes = (HasAPIKey,)
    # permission_classes = (AllowAny,)

    def post(self, request):
        try:
            data = request.data
            response = Response()
            username = data.get('username', None)
            password = data.get('password', None)
            try:
                User.objects.get(email = username)
            except User.DoesNotExist:
                return Response({"msg": "User Credentails don't exist ! "}, status=status.HTTP_400_BAD_REQUEST)
            user = authenticate(username=username, password=password)
            if user.is_approved:
                if user is not None:
                    role, roles = extract_patient_role(user.roles.all())
                    permissions = combine_role_permissions(roles)
                    data = get_tokens_for_user(user, is_patient=False)
                    set_access_cookies(response, data['access'])
                    set_refresh_cookies(response, data['refresh'])
                    csrf.get_token(request)
                    data = UserSerializer(user, context={'request': request}).data
                    data['roles'] = RoleSerializer(roles, many=True).data
                    data['permissions'] = permissions
                    response.status_code = status.HTTP_200_OK
                    response.data = {"msg": "Login successfully", "user": data}
                    user.last_login = timezone.now()
                    user.save()
                    return response
                else:
                    return Response({"msg": "Invalid credentials"}, status=status.HTTP_404_NOT_FOUND)
            else:
                return Response({"msg": "Please verify account to log in"}, status=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            print(e)
            return Response({"msg": "Unable to Login"}, status=status.HTTP_400_BAD_REQUEST)

This is the axios instance for logging in:

import Vue from "vue";

import axios from "axios";

function getCookie(name) {
  var cookieValue = null;
  if (document.cookie && document.cookie !== "") {
    var cookies = document.cookie.split(";");
    for (var i = 0; i < cookies.length; i++) {
      var cookie = cookies[i].trim();
      if (cookie.substring(0, name.length + 1) === name + "=") {
        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
        break;
      }
    }
  }
  return cookieValue;
}

const axiosIns2 = axios.create({
  baseURL: process.env.VUE_APP_API,
  withCredentials: true,
  xsrfCookieName: "csrftoken",
  xsrfHeaderName: "X-CSRFTOKEN",
  headers: {
    "X-CSRFTOKEN": getCookie("csrftoken"),
    Authorization: "Api-Key " + process.env.VUE_APP_KEY,
  },
});

Vue.prototype.$http = axiosIns2;

export default axiosIns2;

And this is the axios instance for other authenticated requests:

import Vue from "vue";

import axios from "axios";

function getCookie(name) {
  var cookieValue = null;
  if (document.cookie && document.cookie !== "") {
    var cookies = document.cookie.split(";");
    for (var i = 0; i < cookies.length; i++) {
      var cookie = cookies[i].trim();
      if (cookie.substring(0, name.length + 1) === name + "=") {
        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
        break;
      }
    }
  }
  return cookieValue;
}

const axiosIns = axios.create({
  baseURL: process.env.VUE_APP_API,
  withCredentials: true,
  xsrfCookieName: "csrftoken",
  xsrfHeaderName: "X-CSRFTOKEN",
  headers: {
    "X-CSRFTOKEN": getCookie("csrftoken"),
  },
});

const COOKIE_EXPIRED_MSG = "Token is invalid or expired";

axiosIns.interceptors.response.use(
  (response) => {
    return response;
  },
  async (error) => {
    let error_message = null;
    if (error.response.data.messages) {
      error_message = error.response.data.messages[0].message;
    }
    switch (error.response.status) {
      case 401:
        if (!error.config.retry && error_message === COOKIE_EXPIRED_MSG) {
          error.config.retry = true;
          // axiosIns.defaults.xsrfCookieName = "csrf_refresh_token";
          await axiosIns.post("/refresh");
          // axiosIns.defaults.xsrfCookieName = "csrf_access_token";
          return axiosIns(error.config);
        } else {
          throw new Error("Error");
        }
        break;
      default:
        break;
    }
    return Promise.reject(error);
  }
);

Vue.prototype.$http = axiosIns;

export default axiosIns;

VPS OS Details: Ubuntu 18.04.6

I currently don't have access to VPS, i believe this issue is due to SIMPLE_JWT settings in settings.py.

The project runs perfectly fine with these settings locally, but it doesn't send cookies on login when hosted. It does give 200 on login though.

Hosted Via: Nginx , Gunicorn

Back to Top