Skip to content

Commit 70c6e88

Browse files
author
IndominusByte
committed
set cookie in response directly
1 parent c902112 commit 70c6e88

File tree

2 files changed

+124
-17
lines changed

2 files changed

+124
-17
lines changed

fastapi_jwt_auth/auth_jwt.py

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -315,12 +315,18 @@ def _get_csrf_token(self,encoded_token: str) -> str:
315315
"""
316316
return self._verified_token(encoded_token)['csrf']
317317

318-
def set_access_cookies(self,encoded_access_token: str, max_age: Optional[int] = None) -> None:
318+
def set_access_cookies(
319+
self,
320+
encoded_access_token: str,
321+
response: Optional[Response] = None,
322+
max_age: Optional[int] = None
323+
) -> None:
319324
"""
320325
Configures the response to set access token in a cookie.
321326
this will also set the CSRF double submit values in a separate cookie
322327
323328
:param encoded_access_token: The encoded access token to set in the cookies
329+
:param response: The FastAPI response object to set the access cookies in
324330
:param max_age: The max age of the cookie value should be the number of seconds (integer)
325331
"""
326332
if not self.jwt_in_cookies:
@@ -330,9 +336,13 @@ def set_access_cookies(self,encoded_access_token: str, max_age: Optional[int] =
330336

331337
if max_age and not isinstance(max_age,int):
332338
raise TypeError("max_age must be a integer")
339+
if response and not isinstance(response,Response):
340+
raise TypeError("The response must be an object response FastAPI")
341+
342+
response = response or self._response
333343

334344
# Set the access JWT in the cookie
335-
self._response.set_cookie(
345+
response.set_cookie(
336346
self._access_cookie_key,
337347
encoded_access_token,
338348
max_age=max_age or self._cookie_max_age,
@@ -345,7 +355,7 @@ def set_access_cookies(self,encoded_access_token: str, max_age: Optional[int] =
345355

346356
# If enabled, set the csrf double submit access cookie
347357
if self._cookie_csrf_protect:
348-
self._response.set_cookie(
358+
response.set_cookie(
349359
self._access_csrf_cookie_key,
350360
self._get_csrf_token(encoded_access_token),
351361
max_age=max_age or self._cookie_max_age,
@@ -356,12 +366,18 @@ def set_access_cookies(self,encoded_access_token: str, max_age: Optional[int] =
356366
samesite=self._cookie_samesite
357367
)
358368

359-
def set_refresh_cookies(self, encoded_refresh_token: str, max_age: Optional[int] = None) -> None:
369+
def set_refresh_cookies(
370+
self,
371+
encoded_refresh_token: str,
372+
response: Optional[Response] = None,
373+
max_age: Optional[int] = None
374+
) -> None:
360375
"""
361376
Configures the response to set refresh token in a cookie.
362377
this will also set the CSRF double submit values in a separate cookie
363378
364379
:param encoded_refresh_token: The encoded refresh token to set in the cookies
380+
:param response: The FastAPI response object to set the refresh cookies in
365381
:param max_age: The max age of the cookie value should be the number of seconds (integer)
366382
"""
367383
if not self.jwt_in_cookies:
@@ -371,9 +387,13 @@ def set_refresh_cookies(self, encoded_refresh_token: str, max_age: Optional[int]
371387

372388
if max_age and not isinstance(max_age,int):
373389
raise TypeError("max_age must be a integer")
390+
if response and not isinstance(response,Response):
391+
raise TypeError("The response must be an object response FastAPI")
392+
393+
response = response or self._response
374394

375395
# Set the refresh JWT in the cookie
376-
self._response.set_cookie(
396+
response.set_cookie(
377397
self._refresh_cookie_key,
378398
encoded_refresh_token,
379399
max_age=max_age or self._cookie_max_age,
@@ -386,7 +406,7 @@ def set_refresh_cookies(self, encoded_refresh_token: str, max_age: Optional[int]
386406

387407
# If enabled, set the csrf double submit refresh cookie
388408
if self._cookie_csrf_protect:
389-
self._response.set_cookie(
409+
response.set_cookie(
390410
self._refresh_csrf_cookie_key,
391411
self._get_csrf_token(encoded_refresh_token),
392412
max_age=max_age or self._cookie_max_age,
@@ -397,52 +417,68 @@ def set_refresh_cookies(self, encoded_refresh_token: str, max_age: Optional[int]
397417
samesite=self._cookie_samesite
398418
)
399419

400-
def unset_jwt_cookies(self) -> None:
420+
def unset_jwt_cookies(self,response: Optional[Response] = None) -> None:
401421
"""
402422
Unset (delete) all jwt stored in a cookie
423+
424+
:param response: The FastAPI response object to delete the JWT cookies in.
403425
"""
404-
self.unset_access_cookies()
405-
self.unset_refresh_cookies()
426+
self.unset_access_cookies(response)
427+
self.unset_refresh_cookies(response)
406428

407-
def unset_access_cookies(self) -> None:
429+
def unset_access_cookies(self,response: Optional[Response] = None) -> None:
408430
"""
409431
Remove access token and access CSRF double submit from the response cookies
432+
433+
:param response: The FastAPI response object to delete the access cookies in.
410434
"""
411435
if not self.jwt_in_cookies:
412436
raise RuntimeWarning(
413437
"unset_access_cookies() called without 'authjwt_token_location' configured to use cookies"
414438
)
415439

416-
self._response.delete_cookie(
440+
if response and not isinstance(response,Response):
441+
raise TypeError("The response must be an object response FastAPI")
442+
443+
response = response or self._response
444+
445+
response.delete_cookie(
417446
self._access_cookie_key,
418447
path=self._access_cookie_path,
419448
domain=self._cookie_domain
420449
)
421450

422451
if self._cookie_csrf_protect:
423-
self._response.delete_cookie(
452+
response.delete_cookie(
424453
self._access_csrf_cookie_key,
425454
path=self._access_csrf_cookie_path,
426455
domain=self._cookie_domain
427456
)
428457

429-
def unset_refresh_cookies(self) -> None:
458+
def unset_refresh_cookies(self,response: Optional[Response] = None) -> None:
430459
"""
431460
Remove refresh token and refresh CSRF double submit from the response cookies
461+
462+
:param response: The FastAPI response object to delete the refresh cookies in.
432463
"""
433464
if not self.jwt_in_cookies:
434465
raise RuntimeWarning(
435466
"unset_refresh_cookies() called without 'authjwt_token_location' configured to use cookies"
436467
)
437468

438-
self._response.delete_cookie(
469+
if response and not isinstance(response,Response):
470+
raise TypeError("The response must be an object response FastAPI")
471+
472+
response = response or self._response
473+
474+
response.delete_cookie(
439475
self._refresh_cookie_key,
440476
path=self._refresh_cookie_path,
441477
domain=self._cookie_domain
442478
)
443479

444480
if self._cookie_csrf_protect:
445-
self._response.delete_cookie(
481+
response.delete_cookie(
446482
self._refresh_csrf_cookie_key,
447483
path=self._refresh_csrf_cookie_path,
448484
domain=self._cookie_domain

tests/test_cookies.py

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,52 @@ def all_token(Authorize: AuthJWT = Depends()):
2424
Authorize.set_refresh_cookies(refresh_token)
2525
return {"msg":"all token"}
2626

27+
@app.get('/all-token-response')
28+
def all_token_response(Authorize: AuthJWT = Depends()):
29+
access_token = Authorize.create_access_token(subject=1,fresh=True)
30+
refresh_token = Authorize.create_refresh_token(subject=1)
31+
response = JSONResponse(content={"msg":"all token"})
32+
Authorize.set_access_cookies(access_token,response)
33+
Authorize.set_refresh_cookies(refresh_token,response)
34+
return response
35+
2736
@app.get('/access-token')
2837
def access_token(Authorize: AuthJWT = Depends()):
2938
access_token = Authorize.create_access_token(subject=1)
3039
Authorize.set_access_cookies(access_token)
3140
return {"msg":"access token"}
3241

42+
@app.get('/access-token-response')
43+
def access_token_response(Authorize: AuthJWT = Depends()):
44+
access_token = Authorize.create_access_token(subject=1)
45+
response = JSONResponse(content={"msg":"access token"})
46+
Authorize.set_access_cookies(access_token,response)
47+
return response
48+
3349
@app.get('/refresh-token')
3450
def refresh_token(Authorize: AuthJWT = Depends()):
3551
refresh_token = Authorize.create_refresh_token(subject=1)
3652
Authorize.set_refresh_cookies(refresh_token)
3753
return {"msg":"refresh token"}
3854

55+
@app.get('/refresh-token-response')
56+
def refresh_token_response(Authorize: AuthJWT = Depends()):
57+
refresh_token = Authorize.create_refresh_token(subject=1)
58+
response = JSONResponse(content={"msg":"refresh token"})
59+
Authorize.set_refresh_cookies(refresh_token,response)
60+
return response
61+
3962
@app.get('/unset-all-token')
4063
def unset_all_token(Authorize: AuthJWT = Depends()):
4164
Authorize.unset_jwt_cookies()
4265
return {"msg":"unset all token"}
4366

67+
@app.get('/unset-all-token-response')
68+
def unset_all_token_response(Authorize: AuthJWT = Depends()):
69+
response = JSONResponse(content={"msg":"unset all token"})
70+
Authorize.unset_jwt_cookies(response)
71+
return response
72+
4473
@app.get('/unset-access-token')
4574
def unset_access_token(Authorize: AuthJWT = Depends()):
4675
Authorize.unset_access_cookies()
@@ -96,7 +125,29 @@ def get_cookie_location():
96125
with pytest.raises(TypeError,match=r"max_age"):
97126
Authorize.set_refresh_cookies(token,max_age="string")
98127

99-
@pytest.mark.parametrize("url",["/access-token","/refresh-token"])
128+
def test_set_unset_cookies_not_valid_type_response(Authorize):
129+
@AuthJWT.load_config
130+
def get_cookie_location():
131+
return [("authjwt_token_location",{'cookies'}),("authjwt_secret_key","secret")]
132+
133+
token = Authorize.create_access_token(subject=1)
134+
135+
with pytest.raises(TypeError,match=r"response"):
136+
Authorize.set_access_cookies(token,response={"msg":"hello"})
137+
138+
with pytest.raises(TypeError,match=r"response"):
139+
Authorize.set_refresh_cookies(token,response={"msg":"hello"})
140+
141+
with pytest.raises(TypeError,match=r"response"):
142+
Authorize.unset_jwt_cookies({"msg":"hello"})
143+
144+
with pytest.raises(TypeError,match=r"response"):
145+
Authorize.unset_access_cookies({"msg":"hello"})
146+
147+
with pytest.raises(TypeError,match=r"response"):
148+
Authorize.unset_refresh_cookies({"msg":"hello"})
149+
150+
@pytest.mark.parametrize("url",["/access-token","/refresh-token","/access-token-response","/refresh-token-response"])
100151
def test_set_cookie_csrf_protect_false(url,client):
101152
@AuthJWT.load_config
102153
def get_cookie_location():
@@ -110,7 +161,7 @@ def get_cookie_location():
110161
response = client.get(url)
111162
assert response.cookies.get("csrf_{}_token".format(cookie_key)) is None
112163

113-
@pytest.mark.parametrize("url",["/access-token","/refresh-token"])
164+
@pytest.mark.parametrize("url",["/access-token","/refresh-token","/access-token-response","/refresh-token-response"])
114165
def test_set_cookie_csrf_protect_true(url,client):
115166
@AuthJWT.load_config
116167
def get_cookie_location():
@@ -140,6 +191,26 @@ def get_cookie_location():
140191
assert response.cookies.get("refresh_token_cookie") is None
141192
assert response.cookies.get("csrf_refresh_token") is None
142193

194+
def test_unset_all_cookie_response(client):
195+
@AuthJWT.load_config
196+
def get_cookie_location():
197+
return [("authjwt_token_location",{'cookies'}),("authjwt_secret_key","secret")]
198+
199+
response = client.get('/all-token-response')
200+
assert response.cookies.get("access_token_cookie") is not None
201+
assert response.cookies.get("csrf_access_token") is not None
202+
203+
assert response.cookies.get("refresh_token_cookie") is not None
204+
assert response.cookies.get("csrf_refresh_token") is not None
205+
206+
response = client.get('/unset-all-token-response')
207+
208+
assert response.cookies.get("access_token_cookie") is None
209+
assert response.cookies.get("csrf_access_token") is None
210+
211+
assert response.cookies.get("refresh_token_cookie") is None
212+
assert response.cookies.get("csrf_refresh_token") is None
213+
143214
def test_custom_cookie_key(client):
144215
@AuthJWT.load_config
145216
def get_cookie_location():

0 commit comments

Comments
 (0)