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):
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?