@@ -350,6 +350,38 @@ async def test_oidc_token_without_email_verified_claim(
350350 assert userinfo .get ("email" ) == mock_jwt_payload .claims .get ("email" )
351351
352352
353+ async def test_oidc_valid_no_edit_user_role_if_claim_not_in_userinfo (
354+ mocker ,
355+ mock_oidc_enabled ,
356+ mock_token ,
357+ mock_openid_configuration ,
358+ ):
359+ """Test that role is not changed for existing user on login if OIDC role claim is configured but not provided by the OIDC provider."""
360+ mocker .patch ("handler.auth.base_handler.OIDC_CLAIM_ROLES" , "roles" )
361+ mocker .patch ("handler.auth.base_handler.OIDC_ROLE_ADMIN" , "admin" )
362+ # The OIDC provider does NOT include the roles claim in userinfo
363+ mock_token ["userinfo" ].pop ("roles" , None )
364+ mock_user = MagicMock (enabled = True , role = Role .ADMIN )
365+ mocker .patch (
366+ "handler.database.db_user_handler.get_user_by_email" , return_value = mock_user
367+ )
368+ mock_edit_user = mocker .patch (
369+ "handler.database.db_user_handler.update_user" , return_value = mock_user
370+ )
371+ mocker .patch .object (
372+ StarletteOAuth2App ,
373+ "load_server_metadata" ,
374+ return_value = mock_openid_configuration ,
375+ )
376+
377+ oidc_handler = OpenIDHandler ()
378+ user , _ = await oidc_handler .get_current_active_user_from_openid_token (mock_token )
379+
380+ # The user's existing ADMIN role should be preserved
381+ mock_edit_user .assert_not_called ()
382+ assert user .role == Role .ADMIN
383+
384+
353385async def test_oidc_invalid_token_signature (mock_oidc_enabled ):
354386 """Test token decoding raises exception for invalid signature."""
355387 oidc_handler = OpenIDHandler ()
0 commit comments