Skip to content

Commit 4aa3dd5

Browse files
committed
adding dev-v0.20.0 tag to this commit to ensure building
1 parent fcf4c35 commit 4aa3dd5

File tree

7 files changed

+529
-34
lines changed

7 files changed

+529
-34
lines changed

html/supertokens_python/constants.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ <h1 class="title">Module <code>supertokens_python.constants</code></h1>
4242
from __future__ import annotations
4343

4444
SUPPORTED_CDI_VERSIONS = [&#34;3.0&#34;]
45-
VERSION = &#34;0.19.0&#34;
45+
VERSION = &#34;0.20.0&#34;
4646
TELEMETRY = &#34;/telemetry&#34;
4747
USER_COUNT = &#34;/users/count&#34;
4848
USER_DELETE = &#34;/user/remove&#34;

html/supertokens_python/recipe/session/cookie_and_header.html

Lines changed: 206 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ <h1 class="title">Module <code>supertokens_python.recipe.session.cookie_and_head
4646

4747
from typing_extensions import Literal
4848

49+
from supertokens_python.recipe.session.exceptions import (
50+
raise_clear_duplicate_session_cookies_exception,
51+
)
52+
from supertokens_python.recipe.session.interfaces import ResponseMutator
53+
4954
from .constants import (
5055
ACCESS_CONTROL_EXPOSE_HEADERS,
5156
ACCESS_TOKEN_COOKIE_KEY,
@@ -139,9 +144,9 @@ <h1 class="title">Module <code>supertokens_python.recipe.session.cookie_and_head
139144
expires: int,
140145
path_type: Literal[&#34;refresh_token_path&#34;, &#34;access_token_path&#34;],
141146
request: BaseRequest,
147+
domain: Optional[str],
142148
user_context: Dict[str, Any],
143149
):
144-
domain = config.cookie_domain
145150
secure = config.cookie_secure
146151
same_site = config.get_cookie_same_site(request, user_context)
147152
path = &#34;&#34;
@@ -169,10 +174,21 @@ <h1 class="title">Module <code>supertokens_python.recipe.session.cookie_and_head
169174
expires: int,
170175
path_type: Literal[&#34;refresh_token_path&#34;, &#34;access_token_path&#34;],
171176
request: BaseRequest,
177+
domain: Optional[str] = None,
172178
):
179+
domain = domain if domain is not None else config.cookie_domain
180+
173181
def mutator(response: BaseResponse, user_context: Dict[str, Any]):
174182
return _set_cookie(
175-
response, config, key, value, expires, path_type, request, user_context
183+
response,
184+
config,
185+
key,
186+
value,
187+
expires,
188+
path_type,
189+
request,
190+
domain,
191+
user_context,
176192
)
177193

178194
return mutator
@@ -322,6 +338,7 @@ <h1 class="title">Module <code>supertokens_python.recipe.session.cookie_and_head
322338
expires,
323339
&#34;refresh_token_path&#34; if token_type == &#34;refresh&#34; else &#34;access_token_path&#34;,
324340
request,
341+
config.cookie_domain,
325342
user_context,
326343
)
327344
elif transfer_method == &#34;header&#34;:
@@ -425,7 +442,97 @@ <h1 class="title">Module <code>supertokens_python.recipe.session.cookie_and_head
425442
&#34;header&#34;,
426443
request,
427444
user_context,
428-
)</code></pre>
445+
)
446+
447+
448+
# This function addresses an edge case where changing the cookie_domain config on the server can
449+
# lead to session integrity issues. For instance, if the API server URL is &#39;api.example.com&#39;
450+
# with a cookie domain of &#39;.example.com&#39;, and the server updates the cookie domain to &#39;api.example.com&#39;,
451+
# the client may retain cookies with both &#39;.example.com&#39; and &#39;api.example.com&#39; domains.
452+
453+
# Consequently, if the server chooses the older cookie, session invalidation occurs, potentially
454+
# resulting in an infinite refresh loop. To fix this, users are asked to specify &#34;older_cookie_domain&#34; in
455+
# the config.
456+
457+
# This function checks for multiple cookies with the same name and clears the cookies for the older domain.
458+
def clear_session_cookies_from_older_cookie_domain(
459+
request: BaseRequest, config: SessionConfig, user_context: Dict[str, Any]
460+
):
461+
allowed_transfer_method = config.get_token_transfer_method(
462+
request, False, user_context
463+
)
464+
# If the transfer method is &#39;header&#39;, there&#39;s no need to clear cookies immediately, even if there are multiple in the request.
465+
if allowed_transfer_method == &#34;header&#34;:
466+
return
467+
468+
did_clear_cookies = False
469+
response_mutators: List[ResponseMutator] = []
470+
471+
token_types: List[TokenType] = [&#34;access&#34;, &#34;refresh&#34;]
472+
for token_type in token_types:
473+
if has_multiple_cookies_for_token_type(request, token_type):
474+
# If a request has multiple session cookies and &#39;older_cookie_domain&#39; is
475+
# unset, we can&#39;t identify the correct cookie for refreshing the session.
476+
# Using the wrong cookie can cause an infinite refresh loop. To avoid this,
477+
# we throw a 500 error asking the user to set &#39;older_cookie_domain&#39;&#39;.
478+
if config.older_cookie_domain is None:
479+
raise Exception(
480+
&#34;The request contains multiple session cookies. This may happen if you&#39;ve changed the &#39;cookie_domain&#39; setting in your configuration. To clear tokens from the previous domain, set &#39;older_cookie_domain&#39; in your config.&#34;
481+
)
482+
483+
log_debug_message(
484+
&#34;Clearing duplicate %s cookie with domain %s&#34;,
485+
token_type,
486+
config.cookie_domain,
487+
)
488+
response_mutators.append(
489+
set_cookie_response_mutator(
490+
config,
491+
get_cookie_name_from_token_type(token_type),
492+
&#34;&#34;,
493+
0,
494+
&#34;refresh_token_path&#34;
495+
if token_type == &#34;refresh&#34;
496+
else &#34;access_token_path&#34;,
497+
request,
498+
domain=config.older_cookie_domain,
499+
)
500+
)
501+
did_clear_cookies = True
502+
if did_clear_cookies:
503+
raise_clear_duplicate_session_cookies_exception(
504+
&#34;The request contains multiple session cookies. We are clearing the cookie from older_cookie_domain. Session will be refreshed in the next refresh call.&#34;,
505+
response_mutators=response_mutators,
506+
)
507+
508+
509+
def has_multiple_cookies_for_token_type(
510+
request: BaseRequest, token_type: TokenType
511+
) -&gt; bool:
512+
cookie_string = request.get_header(&#34;cookie&#34;)
513+
if cookie_string is None:
514+
return False
515+
516+
cookies = _parse_cookie_string_from_request_header_allow_duplicates(cookie_string)
517+
cookie_name = get_cookie_name_from_token_type(token_type)
518+
return cookie_name in cookies and len(cookies[cookie_name]) &gt; 1
519+
520+
521+
def _parse_cookie_string_from_request_header_allow_duplicates(
522+
cookie_string: str,
523+
) -&gt; Dict[str, List[str]]:
524+
cookies: Dict[str, List[str]] = {}
525+
cookie_pairs = cookie_string.split(&#34;;&#34;)
526+
for cookie_pair in cookie_pairs:
527+
name_value = cookie_pair.split(&#34;=&#34;)
528+
if len(name_value) != 2:
529+
raise Exception(&#34;Invalid cookie string in request header&#34;)
530+
name, value = unquote(name_value[0].strip()), unquote(name_value[1].strip())
531+
if name in cookies:
532+
cookies[name].append(value)
533+
else:
534+
cookies[name] = [value]
535+
return cookies</code></pre>
429536
</details>
430537
</section>
431538
<section>
@@ -507,6 +614,66 @@ <h2 class="section-title" id="header-functions">Functions</h2>
507614
)</code></pre>
508615
</details>
509616
</dd>
617+
<dt id="supertokens_python.recipe.session.cookie_and_header.clear_session_cookies_from_older_cookie_domain"><code class="name flex">
618+
<span>def <span class="ident">clear_session_cookies_from_older_cookie_domain</span></span>(<span>request: BaseRequest, config: SessionConfig, user_context: Dict[str, Any])</span>
619+
</code></dt>
620+
<dd>
621+
<div class="desc"></div>
622+
<details class="source">
623+
<summary>
624+
<span>Expand source code</span>
625+
</summary>
626+
<pre><code class="python">def clear_session_cookies_from_older_cookie_domain(
627+
request: BaseRequest, config: SessionConfig, user_context: Dict[str, Any]
628+
):
629+
allowed_transfer_method = config.get_token_transfer_method(
630+
request, False, user_context
631+
)
632+
# If the transfer method is &#39;header&#39;, there&#39;s no need to clear cookies immediately, even if there are multiple in the request.
633+
if allowed_transfer_method == &#34;header&#34;:
634+
return
635+
636+
did_clear_cookies = False
637+
response_mutators: List[ResponseMutator] = []
638+
639+
token_types: List[TokenType] = [&#34;access&#34;, &#34;refresh&#34;]
640+
for token_type in token_types:
641+
if has_multiple_cookies_for_token_type(request, token_type):
642+
# If a request has multiple session cookies and &#39;older_cookie_domain&#39; is
643+
# unset, we can&#39;t identify the correct cookie for refreshing the session.
644+
# Using the wrong cookie can cause an infinite refresh loop. To avoid this,
645+
# we throw a 500 error asking the user to set &#39;older_cookie_domain&#39;&#39;.
646+
if config.older_cookie_domain is None:
647+
raise Exception(
648+
&#34;The request contains multiple session cookies. This may happen if you&#39;ve changed the &#39;cookie_domain&#39; setting in your configuration. To clear tokens from the previous domain, set &#39;older_cookie_domain&#39; in your config.&#34;
649+
)
650+
651+
log_debug_message(
652+
&#34;Clearing duplicate %s cookie with domain %s&#34;,
653+
token_type,
654+
config.cookie_domain,
655+
)
656+
response_mutators.append(
657+
set_cookie_response_mutator(
658+
config,
659+
get_cookie_name_from_token_type(token_type),
660+
&#34;&#34;,
661+
0,
662+
&#34;refresh_token_path&#34;
663+
if token_type == &#34;refresh&#34;
664+
else &#34;access_token_path&#34;,
665+
request,
666+
domain=config.older_cookie_domain,
667+
)
668+
)
669+
did_clear_cookies = True
670+
if did_clear_cookies:
671+
raise_clear_duplicate_session_cookies_exception(
672+
&#34;The request contains multiple session cookies. We are clearing the cookie from older_cookie_domain. Session will be refreshed in the next refresh call.&#34;,
673+
response_mutators=response_mutators,
674+
)</code></pre>
675+
</details>
676+
</dd>
510677
<dt id="supertokens_python.recipe.session.cookie_and_header.clear_session_from_all_token_transfer_methods"><code class="name flex">
511678
<span>def <span class="ident">clear_session_from_all_token_transfer_methods</span></span>(<span>response: BaseResponse, recipe: SessionRecipe, request: BaseRequest, user_context: Dict[str, Any])</span>
512679
</code></dt>
@@ -699,6 +866,27 @@ <h2 class="section-title" id="header-functions">Functions</h2>
699866
raise Exception(&#34;Should never happen: Unknown transferMethod: &#34; + transfer_method)</code></pre>
700867
</details>
701868
</dd>
869+
<dt id="supertokens_python.recipe.session.cookie_and_header.has_multiple_cookies_for_token_type"><code class="name flex">
870+
<span>def <span class="ident">has_multiple_cookies_for_token_type</span></span>(<span>request: BaseRequest, token_type: TokenType) ‑> bool</span>
871+
</code></dt>
872+
<dd>
873+
<div class="desc"></div>
874+
<details class="source">
875+
<summary>
876+
<span>Expand source code</span>
877+
</summary>
878+
<pre><code class="python">def has_multiple_cookies_for_token_type(
879+
request: BaseRequest, token_type: TokenType
880+
) -&gt; bool:
881+
cookie_string = request.get_header(&#34;cookie&#34;)
882+
if cookie_string is None:
883+
return False
884+
885+
cookies = _parse_cookie_string_from_request_header_allow_duplicates(cookie_string)
886+
cookie_name = get_cookie_name_from_token_type(token_type)
887+
return cookie_name in cookies and len(cookies[cookie_name]) &gt; 1</code></pre>
888+
</details>
889+
</dd>
702890
<dt id="supertokens_python.recipe.session.cookie_and_header.remove_header"><code class="name flex">
703891
<span>def <span class="ident">remove_header</span></span>(<span>response: BaseResponse, key: str)</span>
704892
</code></dt>
@@ -714,7 +902,7 @@ <h2 class="section-title" id="header-functions">Functions</h2>
714902
</details>
715903
</dd>
716904
<dt id="supertokens_python.recipe.session.cookie_and_header.set_cookie_response_mutator"><code class="name flex">
717-
<span>def <span class="ident">set_cookie_response_mutator</span></span>(<span>config: SessionConfig, key: str, value: str, expires: int, path_type: "Literal['refresh_token_path', 'access_token_path']", request: BaseRequest)</span>
905+
<span>def <span class="ident">set_cookie_response_mutator</span></span>(<span>config: SessionConfig, key: str, value: str, expires: int, path_type: "Literal['refresh_token_path', 'access_token_path']", request: BaseRequest, domain: Optional[str] = None)</span>
718906
</code></dt>
719907
<dd>
720908
<div class="desc"></div>
@@ -729,10 +917,21 @@ <h2 class="section-title" id="header-functions">Functions</h2>
729917
expires: int,
730918
path_type: Literal[&#34;refresh_token_path&#34;, &#34;access_token_path&#34;],
731919
request: BaseRequest,
920+
domain: Optional[str] = None,
732921
):
922+
domain = domain if domain is not None else config.cookie_domain
923+
733924
def mutator(response: BaseResponse, user_context: Dict[str, Any]):
734925
return _set_cookie(
735-
response, config, key, value, expires, path_type, request, user_context
926+
response,
927+
config,
928+
key,
929+
value,
930+
expires,
931+
path_type,
932+
request,
933+
domain,
934+
user_context,
736935
)
737936

738937
return mutator</code></pre>
@@ -828,6 +1027,7 @@ <h2>Index</h2>
8281027
<li><code><a title="supertokens_python.recipe.session.cookie_and_header.access_token_mutator" href="#supertokens_python.recipe.session.cookie_and_header.access_token_mutator">access_token_mutator</a></code></li>
8291028
<li><code><a title="supertokens_python.recipe.session.cookie_and_header.anti_csrf_response_mutator" href="#supertokens_python.recipe.session.cookie_and_header.anti_csrf_response_mutator">anti_csrf_response_mutator</a></code></li>
8301029
<li><code><a title="supertokens_python.recipe.session.cookie_and_header.build_front_token" href="#supertokens_python.recipe.session.cookie_and_header.build_front_token">build_front_token</a></code></li>
1030+
<li><code><a title="supertokens_python.recipe.session.cookie_and_header.clear_session_cookies_from_older_cookie_domain" href="#supertokens_python.recipe.session.cookie_and_header.clear_session_cookies_from_older_cookie_domain">clear_session_cookies_from_older_cookie_domain</a></code></li>
8311031
<li><code><a title="supertokens_python.recipe.session.cookie_and_header.clear_session_from_all_token_transfer_methods" href="#supertokens_python.recipe.session.cookie_and_header.clear_session_from_all_token_transfer_methods">clear_session_from_all_token_transfer_methods</a></code></li>
8321032
<li><code><a title="supertokens_python.recipe.session.cookie_and_header.clear_session_mutator" href="#supertokens_python.recipe.session.cookie_and_header.clear_session_mutator">clear_session_mutator</a></code></li>
8331033
<li><code><a title="supertokens_python.recipe.session.cookie_and_header.clear_session_response_mutator" href="#supertokens_python.recipe.session.cookie_and_header.clear_session_response_mutator">clear_session_response_mutator</a></code></li>
@@ -838,6 +1038,7 @@ <h2>Index</h2>
8381038
<li><code><a title="supertokens_python.recipe.session.cookie_and_header.get_response_header_name_for_token_type" href="#supertokens_python.recipe.session.cookie_and_header.get_response_header_name_for_token_type">get_response_header_name_for_token_type</a></code></li>
8391039
<li><code><a title="supertokens_python.recipe.session.cookie_and_header.get_rid_header" href="#supertokens_python.recipe.session.cookie_and_header.get_rid_header">get_rid_header</a></code></li>
8401040
<li><code><a title="supertokens_python.recipe.session.cookie_and_header.get_token" href="#supertokens_python.recipe.session.cookie_and_header.get_token">get_token</a></code></li>
1041+
<li><code><a title="supertokens_python.recipe.session.cookie_and_header.has_multiple_cookies_for_token_type" href="#supertokens_python.recipe.session.cookie_and_header.has_multiple_cookies_for_token_type">has_multiple_cookies_for_token_type</a></code></li>
8411042
<li><code><a title="supertokens_python.recipe.session.cookie_and_header.remove_header" href="#supertokens_python.recipe.session.cookie_and_header.remove_header">remove_header</a></code></li>
8421043
<li><code><a title="supertokens_python.recipe.session.cookie_and_header.set_cookie_response_mutator" href="#supertokens_python.recipe.session.cookie_and_header.set_cookie_response_mutator">set_cookie_response_mutator</a></code></li>
8431044
<li><code><a title="supertokens_python.recipe.session.cookie_and_header.set_header" href="#supertokens_python.recipe.session.cookie_and_header.set_header">set_header</a></code></li>

0 commit comments

Comments
 (0)