android – How to set django csrf and session cookies in an ionic capacitor app

I am developing an Ionic 6 capacitor app, with a python Django backend. I am trying to set csrf and sessionid cookies to be able to query a backend api. So far everything works well in browser (both desktop and mobile), but does not seem to work in the built app.

I am trying to debug the app on the local network, so during this phase the server is running in http mode on LAN.

On the frontend, I am using Ionic 6 with capacitor, and I have the following setup:

axiosConfig.js

import axios from "axios";

const validateStatus = function (status) {
  return status >= 200 && status < 500; // default
};

axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.xsrfHeaderName = "X-CSRFToken";
axios.defaults.withCredentials = true;
axios.defaults.validateStatus = validateStatus;

export default axios;

The following requests are made from the frontend:

requests.js

import endpoints from "../../helpers/endpoints";
import axios from "../../helpers/axiosConfig";

export default {
  async setCSRFToken() {
    const response = await axios.get(endpoints.csrfToken);
    // csrf token set here
    if (response.status === 200) {
      console.log("CSRF token set");
      return true;
    } else {
      this.error = response.data.detail;
      console.log("Error: ", this.error);
      return false;
    }
  },
  async login(username, password) {
    // sessionid is set here
    const response = await axios.post(endpoints.login, {
      username,
      password,
    });
    if (response.status === 200) {
      this.user = response.data;
      this.isAuthenticated = true;
      this.username = username;
      console.log("User logged in:", this.user);
      return true;
    } else {
      this.error = response.data.detail;
      console.log("Error: ", this.error);
      return false;
    }
  },
  async checkAuth() {
    const response = await axios.get(endpoints.checkAuth);
    // simple validation on the stored sessionid againt the backend
    if (response.status === 200) {
      this.isAuthenticated = true;
      console.log("User logged in");
      return true;
    } else {
      this.isAuthenticated = false;
      this.error = response.data.detail;
      console.log("User not logged in or error: ", this.error);
      return false;
    }
  },
}

The above requests correspond to the following views on the Django backend:

views.py

import json

from django.contrib.auth import authenticate, login, logout
from django.http import JsonResponse
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.http import require_POST

from rest_framework.views import APIView
from rest_framework.authentication import SessionAuthentication
# set csrf_token


@ensure_csrf_cookie
def set_csrf_token(request):
    """
    Set CSRF token for AJAX request
    """
    # csrf cookie set
    return JsonResponse({'detail': 'CSRF token set'})


# user login view
# log user in based on credentials from json post request
@require_POST
def user_login(request):
    """
    Log user in based on credentials from json post request
    """
    data = json.loads(request.body)
    # get username and password from json post request
    username = data.get('username')
    password = data.get('password')
    if username is not None and password is not None:
        user = authenticate(username=username, password=password)
        if user is not None:
            if user.is_active:
                login(request, user)
                # sessionid cookie set 
                return JsonResponse({'detail': 'Logged in'})
            else:
                return JsonResponse({'detail': 'User is not active'}, status=403)
        else:
            return JsonResponse({'detail': 'Invalid credentials'}, status=403)

    return JsonResponse({'detail': 'Invalid credentials'}, status=403)


# logout view
# log user out
def user_logout(request):
    """
    Log user out
    """
    logout(request)
    return JsonResponse({'detail': 'Logged out'})


# check auth class
# check if user is authenticated
class CheckAuth(APIView):
    authentication_classes = [SessionAuthentication]

    def get(self, request):
        '''check if user is authenticated'''
        # sessionid validated
        if request.user.is_authenticated:
            return JsonResponse({'detail': 'Authenticated'})
        else:
            return JsonResponse({'detail': 'Not authenticated'}, status=403)

settings.py

....

DEBUG = True

ALLOWED_HOSTS = ["localhost", "127.0.0.1", "192.168.1.104"] # address of the current machine on lan
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
     ....
     'channels',
    "rest_framework",
    "corsheaders",
MIDDLEWARE = [ ... 
    'django.contrib.sessions.middleware.SessionMiddleware',

    "corsheaders.middleware.CorsMiddleware",

    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    ...
]

CORS_ALLOW_CREDENTIALS = True
CORS_ALLOWED_ORIGINS = [
    "http://localhost:8100",
    "http://localhost:8000",
    "http://127.0.0.1:8000",
    "http://192.168.1.104:8000",
    "http://0.0.0.0:8000",
    "http://192.168.1.104:8100",
    "http://localhost",  # android runtime
]

CSRF_TRUSTED_ORIGINS = [
    "http://localhost:8100",
    "http://localhost:8000",
    "http://127.0.0.1:8000",
    "http://192.168.1.104:8000", 
    "http://0.0.0.0:8000",
    "http://192.168.1.104:8100",
    "http://localhost",  # android runtime

...

The cookies are set without issue in both desktop and Android browser (chrome)

But for some reason are not set in the built Ionic app (webview):
enter image description here

Even though the backend responds with the correct headers:

response headers on setCSRF:

HTTP/1.1 200 OK
Content-Type: application/json
Vary: Cookie, Origin
X-Frame-Options: DENY
Content-Length: 28
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
Set-Cookie: csrftoken=bMVAVgur3C3*****; expires=Fri, 26 May 2023 19:54:06 GMT; Max-Age=31449600; Path=/; SameSite=Lax

The next login request fails with the following error: “Forbidden (CSRF cookie not set.): /user/login/”

What is the correct way to store the cookies for mobile devices in this case?

Otherwise, if cookies don’t work? What alternatives exist to ensure a resume setup between ionic capacitor and drf?

Leave a Comment