Skip to content

Commit cfabd11

Browse files
Merge pull request #369 from supertokens/fix/anti-csrf-no-access-token
fix: Anti csrf should happen only when access token is passed while session is optional
2 parents 90ccc1b + 8751f65 commit cfabd11

File tree

3 files changed

+66
-3
lines changed

3 files changed

+66
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
## [unreleased]
1010

1111

12+
## Fixes
13+
14+
- Anti csrf check should happen only when access token is passed while session is optional
15+
1216
## [0.14.6] - 2023-06-22
1317

1418
### Changes and fixes

supertokens_python/recipe/session/session_request_functions.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ async def get_session_from_request(
153153
do_anti_csrf_check = normalise_http_method(request.method()) != "get"
154154
if request_transfer_method == "header":
155155
do_anti_csrf_check = False
156+
if request_access_token is None:
157+
do_anti_csrf_check = False
156158

157159
if do_anti_csrf_check and config.anti_csrf == "VIA_CUSTOM_HEADER":
158160
if config.anti_csrf == "VIA_CUSTOM_HEADER":

tests/test_session.py

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
# under the License.
1414

1515
from datetime import datetime, timedelta
16-
from typing import Any, Dict, List
16+
from typing import Any, Dict, List, Optional
1717
from unittest.mock import MagicMock
1818

19-
from fastapi import FastAPI
19+
from fastapi import FastAPI, Depends
2020
from fastapi.requests import Request
21+
from fastapi.responses import JSONResponse
2122
from fastapi.testclient import TestClient
2223
from pytest import fixture, mark
2324

@@ -41,7 +42,10 @@
4142
merge_into_access_token_payload,
4243
update_session_data_in_database,
4344
)
44-
from supertokens_python.recipe.session.interfaces import RecipeInterface
45+
from supertokens_python.recipe.session.interfaces import (
46+
RecipeInterface,
47+
SessionContainer,
48+
)
4549
from supertokens_python.recipe.session.jwt import (
4650
parse_jwt_without_signature_verification,
4751
)
@@ -52,6 +56,7 @@
5256
refresh_session,
5357
revoke_session,
5458
)
59+
from supertokens_python.recipe.session.framework.fastapi import verify_session
5560
from tests.utils import clean_st, reset, setup_st, start_st
5661

5762
pytestmark = mark.asyncio
@@ -251,6 +256,12 @@ async def create_api(request: Request): # type: ignore
251256
await async_create_new_session(request, "test-user", {}, {})
252257
return ""
253258

259+
@app.post("/sessioninfo-optional")
260+
async def _session_info(s: Optional[SessionContainer] = Depends(verify_session(session_required=False))): # type: ignore
261+
if s is not None:
262+
return JSONResponse({"session": s.get_handle(), "user_id": s.get_user_id()})
263+
return JSONResponse({"message": "no session"})
264+
254265
return TestClient(app)
255266

256267

@@ -724,3 +735,49 @@ async def test_that_verify_session_doesnt_always_call_core():
724735
AllowedProcessStates.CALLING_SERVICE_IN_VERIFY
725736
in ProcessState.get_instance().history
726737
) # Core got called this time
738+
739+
740+
async def test_anti_csrf_header_via_custom_header_check_happens_only_when_access_token_is_provided(
741+
driver_config_client: TestClient,
742+
):
743+
args = get_st_init_args([session.init(anti_csrf="VIA_CUSTOM_HEADER", get_token_transfer_method=lambda *_: "cookie")]) # type: ignore
744+
init(**args) # type: ignore
745+
start_st()
746+
747+
response = driver_config_client.post("/create")
748+
assert response.status_code == 200
749+
750+
# With access token:
751+
# without RID:
752+
response = driver_config_client.post("/sessioninfo-optional")
753+
assert response.status_code == 401
754+
assert response.json() == {"message": "try refresh token"}
755+
756+
# with RID:
757+
response = driver_config_client.post(
758+
"/sessioninfo-optional",
759+
headers={
760+
"rid": "session",
761+
},
762+
)
763+
assert response.status_code == 200
764+
assert list(response.json()) == ["session", "user_id"]
765+
766+
# Clear access tokens:
767+
driver_config_client.cookies.clear()
768+
769+
# Without access tokens:
770+
# without RID:
771+
response = driver_config_client.post("/sessioninfo-optional")
772+
assert response.status_code == 200
773+
assert response.json() == {"message": "no session"}
774+
775+
# with RID:
776+
response = driver_config_client.post(
777+
"/sessioninfo-optional",
778+
headers={
779+
"rid": "session",
780+
},
781+
)
782+
assert response.status_code == 200
783+
assert response.json() == {"message": "no session"}

0 commit comments

Comments
 (0)