Description
Python SDK with Django session async issue
Having recently discovered logto.io, we tried to implement it in Django.
We tried using TraditionalWebApp process to protect our views, following your official Flask example.
Implementation
First, I implemented /signin
and /callback
routes as shown in the tutorial, wrapping the logto package logic with async_to_sync decorators.
To follow the tutorial, I used session out of views using Django ref : https://docs.djangoproject.com/en/5.0/topics/http/sessions/#using-sessions-out-of-views
views.py
from logto import LogtoClient, LogtoConfig, Storage
import os
from django.views import View
from django.http import HttpResponse, HttpResponseRedirect
from importlib import import_module
from django.conf import settings
SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
session = SessionStore()
class SessionStorage(Storage):
def __init__(self):
pass
def get(self, key: str):
return session.get(key, "")
def set(self, key: str, value: str | None) -> None:
session[key] = value
def delete(self, key: str) -> None:
if key in session:
session.__delitem__(key)
client = LogtoClient(
LogtoConfig(
endpoint=os.environ.get("LOGTO_API_ENDPOINT"),
appId=os.environ.get("LOGTO_API_CLIENT_ID"),
appSecret=os.environ.get("LOGTO_API_SECRET"),
),
storage=SessionStorage(),
)
class SigninView(View):
async def get(self, request):
uri = os.environ.get("LOGTO_API_REDIRECT_URI")
url = await client.signIn(redirectUri=uri)
return HttpResponseRedirect(redirect_to=url)
class CallbackView(View):
async def get(self, request):
absolute_uri = request.build_absolute_uri()
try:
await client.handleSignInCallback(absolute_uri)
return HttpResponseRedirect(
redirect_to=os.environ.get("LOGTO_CALLBACK_URI")
)
except Exception as e:
return HttpResponse("Error: " + str(e))
urls.py
from django.urls import path
from logto_authentication.views import SigninView, CallbackView
urlpatterns = [
path("signin/", SigninView.as_view(), name="signin"),
path("callback/", CallbackView.as_view(), name="callback"),
]
Problem
As soon as synchronous code considered "critical" by Django (database operations with DjangoORM for the most part) is used during an asynchronous code cycle, Django will raise a SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async
error.
Reference on this error : https://docs.djangoproject.com/en/5.0/topics/async/#async-safety
At that point, in the reference, we had option to set the settings to DJANGO_ALLOW_ASYNC_UNSAFE=True
. Doing so make the whole process to work. But this poses a problem, as this deactivated barrier can cause data loss or corruption on other async.
Another option would have been to turn Storage synchronous calls to the session into asynchronous calls to the methods that require them. But your client is not adapted for using async methods inside this class, and we would need to create a new LogtoClient to do so.
settings.py
import os
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
Therefore, my question is : did anyone try TraditionalWebApp process with Django ? Did you find a way to make it work ?
Thanks in advance for your insights, and thanks for all the work you already did there !