I’m having trouble understanding session authentication, and how to get it working for the Django Rest Framework. My situation is that I have some nice frontend pages done in React, that I’ll need to query user-specific data. But I am running the Django backend on the same domain, so I was hoping to just handle login and authentication with django-allauth using the standard template approach.
As an example of the issues I’m facing, here’s a simple DRF view that should just return the details of the currently logged in user:
class UserInfoView(APIView):
authentication_classes = (SessionAuthentication,)
@method_decorator(ensure_csrf_cookie)
def get(self, request: Request, format=None):
print(request.session.items())
user: RequestUserType = request.user
data = {
"logged_in": user.is_authenticated,
"username": user.get_username(),
}
return Response(data)
Using the Django testing client, this seems to be working correctly:
def test_user_info_view(db, client):
"""
Check we can get user info for a logged in user
"""
user = create_test_user(email="user@example.com", password="password")
logged_in = client.login(email="user@example.com", password="password")
assert logged_in
resp = client.get(reverse("user_info"))
assert resp.json()["username"] == "user@example.com"
assert resp.json()["logged_in"]
However, when I try to use DRF’s RequestClient
to simulate the end-user experience,
I can’t seem to get the login to stick, assert data["logged_in"]
is failing and I don’t get the username returned:
def test_create_session(live_server):
"""
Test if we can successfully create an authenticated session using requests -
seems like this will better simulate access by an external user (but maybe unnecessary?)
"""
# TODO: having trouble getting authentication to stick here
user = create_test_user(email="user@example.com", password="password")
client = create_session(email="user@example.com", password="password", base_url=live_server.url)
resp = client.get(live_server.url + reverse("user_info"))
assert resp.ok
data = resp.json()
assert data["logged_in"]
create_session
grabs the CSRF token before logging in:
def create_session(email: str, password: str, base_url: str = "http://localhost:8000") -> RequestsClient:
"""
Create a RequestsClient instance that is authenticated with the site, for testing
frontend interactions etc. Requires handling the CSRF token.
Args:
username: Username to login with
password: Password
base_url: URL for a running instance of the site.
Returns:
A logged in Session instance which can be used for further requests.
Raises:
HTTPError if login request was not successful
"""
login_path = "/accounts/login/"
client = RequestsClient()
# Perform a get request to set the CSRF cookie
get_resp = client.get(base_url + login_path)
csrf_token = get_resp.cookies["csrftoken"]
login_data = {
"csrfmiddlewaretoken": csrf_token,
"login": email,
"password": password,
}
resp = client.post(base_url + login_path, data=login_data)
if resp.ok:
logging.info(f"Logged in successfully as {email}")
else:
print(resp.status_code)
resp.raise_for_status()
Am I missing something about where CSRF tokens need to be passed?
return client