@@ -142,6 +142,78 @@ async def test_refresh_access_token(self, client: TestClient, user: User, mocker
142142 assert event .event_outcome == EventOutcome .SUCCESS
143143 assert response .status_code == http .HTTPStatus .OK
144144
145+ async def test_refresh_access_token__propagates_client_claim (
146+ self , client : TestClient , user : User , mocker : MockerFixture
147+ ):
148+ mocker .patch ("apps.authentication.api.auth.log" )
149+ refresh_token = AuthenticationService .create_refresh_token (
150+ {
151+ "sub" : str (user .id ),
152+ "jti" : str (uuid .uuid4 ()),
153+ "client" : "admin" ,
154+ }
155+ )
156+ response = await client .post (url = self .refresh_access_token_url , data = {"refresh_token" : refresh_token })
157+ assert response .status_code == http .HTTPStatus .OK
158+ result = response .json ()["result" ]
159+ assert result ["refreshToken" ] == refresh_token
160+ access_payload = jwt .decode (
161+ result ["accessToken" ],
162+ settings .authentication .access_token .secret_key ,
163+ algorithms = [settings .authentication .algorithm ],
164+ )
165+ assert access_payload ["client" ] == "admin"
166+
167+ async def test_refresh_access_token__legacy_token_without_client_claim (
168+ self , client : TestClient , user : User , mocker : MockerFixture
169+ ):
170+ mocker .patch ("apps.authentication.api.auth.log" )
171+ refresh_token = AuthenticationService .create_refresh_token (
172+ {
173+ "sub" : str (user .id ),
174+ "jti" : str (uuid .uuid4 ()),
175+ }
176+ )
177+ response = await client .post (url = self .refresh_access_token_url , data = {"refresh_token" : refresh_token })
178+ assert response .status_code == http .HTTPStatus .OK
179+ access_payload = jwt .decode (
180+ response .json ()["result" ]["accessToken" ],
181+ settings .authentication .access_token .secret_key ,
182+ algorithms = [settings .authentication .algorithm ],
183+ )
184+ assert "client" not in access_payload
185+
186+ async def test_refresh_token_key_transition__preserves_client_claim (
187+ self , client : TestClient , user : User , mocker : MockerFixture
188+ ):
189+ token_key = settings .authentication .refresh_token .secret_key
190+ refresh_token = AuthenticationService .create_refresh_token (
191+ {
192+ "sub" : str (user .id ),
193+ "jti" : str (uuid .uuid4 ()),
194+ "client" : "web" ,
195+ }
196+ )
197+ new_token_key = "new token key"
198+ transition_expire_date = datetime .datetime .now (datetime .timezone .utc ).date () + datetime .timedelta (days = 1 )
199+
200+ with mock .patch ("config.settings.authentication.refresh_token" ) as token_settings_mock :
201+ token_settings_mock .secret_key = new_token_key
202+ token_settings_mock .transition_key = token_key
203+ token_settings_mock .transition_expire_date = transition_expire_date
204+ token_settings_mock .expiration = 540
205+
206+ _status_code , new_refresh_token = await self ._request_refresh_token (client , refresh_token )
207+ assert _status_code == http .HTTPStatus .OK
208+ assert new_refresh_token
209+ assert new_refresh_token != refresh_token
210+ refresh_payload = jwt .decode (
211+ new_refresh_token ,
212+ new_token_key ,
213+ algorithms = [settings .authentication .algorithm ],
214+ )
215+ assert refresh_payload ["client" ] == "web"
216+
145217 async def test_login_and_logout_device (self , client : TestClient , user : User ):
146218 device_id = str (uuid .uuid4 ())
147219
0 commit comments