7
7
from uuid import uuid4
8
8
9
9
from django .urls import reverse
10
- from reactpy import component , hooks , html
10
+ from reactpy import component , hooks
11
11
12
12
from reactpy_django .javascript_components import HttpRequest
13
- from reactpy_django .models import SynchronizeSession
13
+ from reactpy_django .models import AuthToken
14
14
15
15
if TYPE_CHECKING :
16
16
from django .contrib .sessions .backends .base import SessionBase
@@ -39,13 +39,13 @@ async def rerender():
39
39
@component
40
40
def session_manager ():
41
41
"""This component can force the client (browser) to switch HTTP sessions,
42
- making it match the websocket session.
42
+ making it match the websocket session, by using a authentication token .
43
43
44
44
Used to force persistent authentication between Django's websocket and HTTP stack."""
45
45
from reactpy_django import config
46
46
47
47
synchronize_requested , set_synchronize_requested = hooks .use_state (False )
48
- uuid = hooks .use_ref ("" )
48
+ token = hooks .use_ref ("" )
49
49
scope = hooks .use_connection ().scope
50
50
51
51
@hooks .use_effect (dependencies = [])
@@ -61,33 +61,32 @@ async def synchronize_session_watchdog():
61
61
This effect will automatically be cancelled if the session is successfully
62
62
switched (via effect dependencies)."""
63
63
if synchronize_requested :
64
- await asyncio .sleep (config .REACTPY_AUTH_SYNC_TIMEOUT + 0.1 )
64
+ await asyncio .sleep (config .REACTPY_AUTH_TOKEN_TIMEOUT + 0.1 )
65
65
await asyncio .to_thread (
66
66
_logger .warning ,
67
- f"Client did not switch sessions within { config .REACTPY_AUTH_SYNC_TIMEOUT } (REACTPY_AUTH_SYNC_TIMEOUT ) seconds." ,
67
+ f"Client did not switch sessions within { config .REACTPY_AUTH_TOKEN_TIMEOUT } (REACTPY_AUTH_TOKEN_TIMEOUT ) seconds." ,
68
68
)
69
69
set_synchronize_requested (False )
70
70
71
71
async def synchronize_session ():
72
- """Event that can command the client to switch HTTP sessions (to match the websocket sessions )."""
72
+ """Event that can command the client to switch HTTP sessions (to match the websocket session )."""
73
73
session : SessionBase | None = scope .get ("session" )
74
74
if not session or not session .session_key :
75
75
return
76
76
77
- # Delete any sessions currently associated with the previous UUID.
78
- # This exists to fix scenarios where...
79
- # 1) Login is called multiple times before the first one is completed.
80
- # 2) Login was called, but the server failed to respond to the HTTP request.
81
- if uuid .current :
82
- with contextlib .suppress (SynchronizeSession .DoesNotExist ):
83
- obj = await SynchronizeSession .objects .aget (uuid = uuid .current )
77
+ # Delete previous token to resolve race conditions where...
78
+ # 1. Login was called multiple times before the first one is completed.
79
+ # 2. Login was called, but the server failed to respond to the HTTP request.
80
+ if token .current :
81
+ with contextlib .suppress (AuthToken .DoesNotExist ):
82
+ obj = await AuthToken .objects .aget (value = token .current )
84
83
await obj .adelete ()
85
84
86
- # Create a fresh UUID
87
- uuid .set_current (str (uuid4 ()))
85
+ # Create a fresh token
86
+ token .set_current (str (uuid4 ()))
88
87
89
88
# Begin the process of synchronizing HTTP and websocket sessions
90
- obj = await SynchronizeSession .objects .acreate (uuid = uuid .current , session_key = session .session_key )
89
+ obj = await AuthToken .objects .acreate (value = token .current , session_key = session .session_key )
91
90
await obj .asave ()
92
91
set_synchronize_requested (True )
93
92
@@ -107,7 +106,7 @@ async def synchronize_session_callback(status_code: int, response: str):
107
106
return HttpRequest (
108
107
{
109
108
"method" : "GET" ,
110
- "url" : reverse ("reactpy:session_manager" , args = [uuid .current ]),
109
+ "url" : reverse ("reactpy:session_manager" , args = [token .current ]),
111
110
"body" : None ,
112
111
"callback" : synchronize_session_callback ,
113
112
},
0 commit comments