Skip to content

Commit 181b97b

Browse files
committed
♻️(backend) align CSRF token header with Django conventions
Update the CSRF header naming to follow Django standards, avoiding duplicated client-side logic with inconsistent header names.
1 parent 6ee89b2 commit 181b97b

2 files changed

Lines changed: 17 additions & 17 deletions

File tree

src/backend/core/addons/viewsets.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def poll(self, request):
106106
"""Poll a session for its current state and, if terminal, consume it.
107107
108108
Authenticates the caller using the addonsSid cookie (set by
109-
/init) together with the X-CSRF-Token header, which must match
109+
/init) together with the X-CSRFToken header, which must match
110110
the CSRF token issued for that session. The session id alone is not
111111
sufficient — both must be presented and must correspond.
112112
@@ -127,7 +127,7 @@ def poll(self, request):
127127
"""
128128

129129
session_id = request.COOKIES.get(settings.ADDONS_SESSION_ID_COOKIE)
130-
submitted_csrf = request.headers.get("X-CSRF-Token")
130+
submitted_csrf = request.headers.get("X-CSRFToken")
131131

132132
if not session_id:
133133
return drf_response.Response(

src/backend/core/tests/addons/test_api_addons.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def test_init_cookie_authorizes_subsequent_poll():
9595

9696
poll_response = api_client.post(
9797
"/api/v1.0/addons/sessions/poll/",
98-
HTTP_X_CSRF_TOKEN=csrf_token,
98+
HTTP_X_CSRFTOKEN=csrf_token,
9999
)
100100

101101
assert poll_response.status_code == 202
@@ -180,7 +180,7 @@ def test_poll_rejects_invalid_csrf_token():
180180

181181
poll_response = api_client.post(
182182
"/api/v1.0/addons/sessions/poll/",
183-
HTTP_X_CSRF_TOKEN="invalid-csrf-token",
183+
HTTP_X_CSRFTOKEN="invalid-csrf-token",
184184
)
185185

186186
# SuspiciousOperation translates to 400 via Django's exception middleware.
@@ -202,7 +202,7 @@ def test_poll_session_not_found(mock_get_session_data):
202202

203203
poll_response = api_client.post(
204204
"/api/v1.0/addons/sessions/poll/",
205-
HTTP_X_CSRF_TOKEN=csrf_token,
205+
HTTP_X_CSRFTOKEN=csrf_token,
206206
)
207207

208208
assert poll_response.status_code == 404
@@ -224,7 +224,7 @@ def test_poll_session_corrupted(mock_get_session_data):
224224

225225
poll_response = api_client.post(
226226
"/api/v1.0/addons/sessions/poll/",
227-
HTTP_X_CSRF_TOKEN=csrf_token,
227+
HTTP_X_CSRFTOKEN=csrf_token,
228228
)
229229

230230
assert poll_response.status_code == 400
@@ -249,7 +249,7 @@ def test_poll_session_authenticated():
249249

250250
poll_response = api_client.post(
251251
"/api/v1.0/addons/sessions/poll/",
252-
HTTP_X_CSRF_TOKEN=csrf_token,
252+
HTTP_X_CSRFTOKEN=csrf_token,
253253
)
254254

255255
assert poll_response.status_code == 200
@@ -271,15 +271,15 @@ def test_poll_session_authenticated():
271271
# Server cleared the addonsSid cookie; APIClient drops it → no credentials.
272272
poll_response = api_client.post(
273273
"/api/v1.0/addons/sessions/poll/",
274-
HTTP_X_CSRF_TOKEN=csrf_token,
274+
HTTP_X_CSRFTOKEN=csrf_token,
275275
)
276276
assert poll_response.status_code == 401
277277

278278
# Replay the original addonsSid: session was evicted on terminal read.
279279
api_client.cookies["addonsSid"] = session_id_cookie.value
280280
poll_response = api_client.post(
281281
"/api/v1.0/addons/sessions/poll/",
282-
HTTP_X_CSRF_TOKEN=csrf_token,
282+
HTTP_X_CSRFTOKEN=csrf_token,
283283
)
284284
assert poll_response.status_code == 404
285285
assert poll_response.json() == {"detail": "Session not found."}
@@ -307,19 +307,19 @@ def test_poll_two_clients_do_not_interfere():
307307
# Each client polls its own session.
308308
poll_a = client_a.post(
309309
"/api/v1.0/addons/sessions/poll/",
310-
HTTP_X_CSRF_TOKEN=csrf_a,
310+
HTTP_X_CSRFTOKEN=csrf_a,
311311
)
312312
poll_b = client_b.post(
313313
"/api/v1.0/addons/sessions/poll/",
314-
HTTP_X_CSRF_TOKEN=csrf_b,
314+
HTTP_X_CSRFTOKEN=csrf_b,
315315
)
316316
assert poll_a.status_code == 202
317317
assert poll_b.status_code == 202
318318

319319
# Cross-use (A's cookie + B's CSRF) must be rejected.
320320
cross_response = client_a.post(
321321
"/api/v1.0/addons/sessions/poll/",
322-
HTTP_X_CSRF_TOKEN=csrf_b,
322+
HTTP_X_CSRFTOKEN=csrf_b,
323323
)
324324
assert cross_response.status_code == 400
325325

@@ -336,13 +336,13 @@ def test_poll_two_clients_do_not_interfere():
336336
):
337337
poll_a = client_a.post(
338338
"/api/v1.0/addons/sessions/poll/",
339-
HTTP_X_CSRF_TOKEN=csrf_a,
339+
HTTP_X_CSRFTOKEN=csrf_a,
340340
)
341341
assert poll_a.status_code == 200
342342

343343
poll_b = client_b.post(
344344
"/api/v1.0/addons/sessions/poll/",
345-
HTTP_X_CSRF_TOKEN=csrf_b,
345+
HTTP_X_CSRFTOKEN=csrf_b,
346346
)
347347
assert poll_b.status_code == 202
348348

@@ -369,14 +369,14 @@ def test_poll_csrf_attack_does_not_disrupt_legitimate_client():
369369
# Fabricated CSRF token
370370
attack_bad_csrf = attacker.post(
371371
"/api/v1.0/addons/sessions/poll/",
372-
HTTP_X_CSRF_TOKEN="attacker-guessed-token",
372+
HTTP_X_CSRFTOKEN="attacker-guessed-token",
373373
)
374374
assert attack_bad_csrf.status_code == 400
375375

376376
# Legitimate client's session is still usable.
377377
legitimate_poll = legitimate.post(
378378
"/api/v1.0/addons/sessions/poll/",
379-
HTTP_X_CSRF_TOKEN=csrf_token,
379+
HTTP_X_CSRFTOKEN=csrf_token,
380380
)
381381
assert legitimate_poll.status_code == 202
382382
assert legitimate_poll.json() == {"state": "pending"}
@@ -508,7 +508,7 @@ def test_exchange_success_enables_poll_to_complete():
508508
# 3. Taskpane's next poll transitions from pending → authenticated.
509509
poll_response = taskpane.post(
510510
"/api/v1.0/addons/sessions/poll/",
511-
HTTP_X_CSRF_TOKEN=csrf_token,
511+
HTTP_X_CSRFTOKEN=csrf_token,
512512
)
513513
assert poll_response.status_code == 200
514514
response_data = poll_response.json()

0 commit comments

Comments
 (0)