@@ -52,10 +52,16 @@ def setUpTestData(cls):
5252 def setUp (self ):
5353 self .oauth2_settings .PKCE_REQUIRED = False
5454
55- def test_reauthorization_revokes_old_tokens_with_auto_approval (self ):
55+ def test_reauthorization_creates_multiple_tokens_with_auto_approval (self ):
5656 """
5757 When a user re-authorizes an application with approval_prompt=auto,
58- old access tokens should be revoked when new ones are issued.
58+ new access tokens are created. This is standard OAuth 2.0 behavior that
59+ supports multiple devices/sessions.
60+
61+ Note: Applications should manage token lifecycle by:
62+ - Using token expiration (ACCESS_TOKEN_EXPIRE_SECONDS setting)
63+ - Explicitly revoking tokens via the revocation endpoint when no longer needed
64+ - Using the cleartokens management command to remove expired tokens
5965 """
6066 self .client .login (username = "test_user" , password = "123456" )
6167
@@ -111,24 +117,25 @@ def test_reauthorization_revokes_old_tokens_with_auto_approval(self):
111117 access_token = token_json .get ("access_token" )
112118 created_tokens .append (access_token )
113119
114- # After 3 authorization flows, we should only have 1 token
115- # The old ones should have been revoked (deleted for AccessTokens)
120+ # After 3 authorization flows, we have 3 tokens.
121+ # This is standard OAuth 2.0 behavior - multiple tokens can coexist.
122+ # Tokens with refresh tokens are not automatically revoked to preserve
123+ # the refresh token flow and support multiple sessions.
116124 remaining_tokens = AccessToken .objects .filter (
117125 user = self .test_user ,
118126 application = self .application ,
119127 ).count ()
120128
121129 self .assertEqual (
122130 remaining_tokens ,
123- 1 ,
124- f"Expected 1 token after re-authorization, but found { remaining_tokens } . "
125- "Old tokens should be revoked (deleted) when new ones are issued." ,
131+ 3 ,
132+ f"Expected 3 tokens after 3 authorizations (standard OAuth behavior), but found { remaining_tokens } ." ,
126133 )
127134
128135 def test_reauthorization_with_force_approval (self ):
129136 """
130- When approval_prompt=force, user must approve each time,
131- but old tokens should still be revoked when new ones are issued .
137+ When approval_prompt=force, user must approve each time.
138+ Multiple tokens can coexist (standard OAuth 2.0 behavior) .
132139 """
133140 self .client .login (username = "test_user" , password = "123456" )
134141
@@ -162,7 +169,7 @@ def test_reauthorization_with_force_approval(self):
162169 "code" : code ,
163170 "redirect_uri" : "http://example.org" ,
164171 "client_id" : self .application .client_id ,
165- "client_secret" : self . application . client_secret ,
172+ "client_secret" : CLEARTEXT_SECRET ,
166173 }
167174 token_response = self .client .post (reverse ("oauth2_provider:token" ), data = token_data )
168175 self .assertEqual (token_response .status_code , 200 )
@@ -186,18 +193,18 @@ def test_reauthorization_with_force_approval(self):
186193 token_response = self .client .post (reverse ("oauth2_provider:token" ), data = token_data )
187194 self .assertEqual (token_response .status_code , 200 )
188195
189- # Should have 1 token (old one revoked/deleted )
196+ # Should have 2 tokens (standard OAuth behavior - one from each authorization )
190197 remaining_tokens = AccessToken .objects .filter (
191198 user = self .test_user ,
192199 application = self .application ,
193200 ).count ()
194201
195- self .assertEqual (remaining_tokens , 1 )
202+ self .assertEqual (remaining_tokens , 2 )
196203
197- def test_reauthorization_with_different_scopes_keeps_separate_tokens (self ):
204+ def test_reauthorization_with_different_scopes_creates_separate_tokens (self ):
198205 """
199- If a user authorizes with different scopes, both tokens should remain valid
200- as they serve different purposes.
206+ When a user authorizes with different scopes, separate tokens are created.
207+ This is standard OAuth 2.0 behavior that allows different scopes for different purposes.
201208 """
202209 self .client .login (username = "test_user" , password = "123456" )
203210
@@ -226,7 +233,7 @@ def test_reauthorization_with_different_scopes_keeps_separate_tokens(self):
226233 "code" : code ,
227234 "redirect_uri" : "http://example.org" ,
228235 "client_id" : self .application .client_id ,
229- "client_secret" : self . application . client_secret ,
236+ "client_secret" : CLEARTEXT_SECRET ,
230237 }
231238 self .client .post (reverse ("oauth2_provider:token" ), data = token_data )
232239
@@ -248,11 +255,13 @@ def test_reauthorization_with_different_scopes_keeps_separate_tokens(self):
248255 self .client .post (reverse ("oauth2_provider:token" ), data = token_data )
249256
250257 # Should have 2 tokens (one for each scope)
258+ # This is standard OAuth behavior allowing different scopes for different purposes
251259 remaining_tokens = AccessToken .objects .filter (
252260 user = self .test_user ,
253261 application = self .application ,
254262 ).count ()
255263
256- # Note: The current behavior might be different. This test documents expected behavior.
257- # Different scopes should keep separate tokens, or the new token should have all scopes.
258- self .assertGreaterEqual (remaining_tokens , 1 )
264+ self .assertEqual (remaining_tokens , 2 ,
265+ "Expected 2 tokens after authorizations with different scopes. "
266+ "Multiple tokens with different scopes are allowed in OAuth 2.0."
267+ )
0 commit comments