Skip to content

Commit 6164955

Browse files
descope[bot]shuni[bot]
andauthored
Add delete_user_tokens and delete_token methods for outbound apps (#729)
Co-authored-by: shuni[bot] <shuni[bot]@users.noreply.github.com>
1 parent b738ee6 commit 6164955

File tree

4 files changed

+177
-0
lines changed

4 files changed

+177
-0
lines changed

descope/management/common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ class MgmtV1:
120120
outbound_application_fetch_tenant_token_path = (
121121
"/v1/mgmt/outbound/app/tenant/token/latest"
122122
)
123+
outbound_application_delete_user_tokens_path = "/v1/mgmt/outbound/user/tokens"
124+
outbound_application_delete_token_path = "/v1/mgmt/outbound/token"
123125

124126
# user
125127
user_create_path = "/v1/mgmt/user/create"

descope/management/outbound_application.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,46 @@ def fetch_tenant_token(
449449
options=options,
450450
)
451451

452+
def delete_user_tokens(
453+
self,
454+
app_id: Optional[str] = None,
455+
user_id: Optional[str] = None,
456+
):
457+
"""
458+
Delete outbound application tokens by app ID and/or user ID.
459+
At least one of app_id or user_id must be provided.
460+
461+
Args:
462+
app_id (str): Optional ID of the outbound application.
463+
user_id (str): Optional ID of the user.
464+
465+
Raise:
466+
AuthException: raised if delete operation fails
467+
"""
468+
params = {}
469+
if app_id:
470+
params["appId"] = app_id
471+
if user_id:
472+
params["userId"] = user_id
473+
uri = MgmtV1.outbound_application_delete_user_tokens_path
474+
self._http.delete(uri, params=params)
475+
476+
def delete_token(
477+
self,
478+
token_id: str,
479+
):
480+
"""
481+
Delete an outbound application token by its ID.
482+
483+
Args:
484+
token_id (str): The ID of the token to delete.
485+
486+
Raise:
487+
AuthException: raised if delete operation fails
488+
"""
489+
uri = MgmtV1.outbound_application_delete_token_path
490+
self._http.delete(uri, params={"id": token_id})
491+
452492
@staticmethod
453493
def _compose_create_update_body(
454494
name: str,

samples/management/outbound_application_sample_app.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,31 @@ def main():
136136
except AuthException as e:
137137
logging.info(f"Tenant token fetch failed {e}")
138138

139+
# DELETE USER TOKENS
140+
141+
try:
142+
logging.info("Going to delete user tokens by app and user id")
143+
descope_client.mgmt.outbound_application.delete_user_tokens(
144+
app_id=outbound_app_id,
145+
user_id="user123",
146+
)
147+
logging.info("User tokens deleted successfully")
148+
149+
except AuthException as e:
150+
logging.info(f"User tokens deletion failed {e}")
151+
152+
# DELETE TOKEN BY ID
153+
154+
try:
155+
logging.info("Going to delete token by id")
156+
descope_client.mgmt.outbound_application.delete_token(
157+
token_id="token-id-123",
158+
)
159+
logging.info("Token deleted successfully")
160+
161+
except AuthException as e:
162+
logging.info(f"Token deletion failed {e}")
163+
139164
# DELETE OUTBOUND APPLICATION
140165

141166
try:

tests/management/test_outbound_application.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,116 @@ def test_compose_create_update_body_with_empty_lists(self):
768768

769769
assert body == expected_body
770770

771+
def test_delete_user_tokens_success(self):
772+
client = DescopeClient(
773+
self.dummy_project_id,
774+
self.public_key_dict,
775+
False,
776+
self.dummy_management_key,
777+
)
778+
779+
with patch("requests.delete") as mock_delete:
780+
network_resp = mock.Mock()
781+
network_resp.ok = True
782+
mock_delete.return_value = network_resp
783+
client.mgmt.outbound_application.delete_user_tokens(
784+
app_id="app123", user_id="user456"
785+
)
786+
787+
mock_delete.assert_called_once()
788+
call_args = mock_delete.call_args
789+
assert call_args[1]["params"]["appId"] == "app123"
790+
assert call_args[1]["params"]["userId"] == "user456"
791+
792+
def test_delete_user_tokens_with_app_id_only(self):
793+
client = DescopeClient(
794+
self.dummy_project_id,
795+
self.public_key_dict,
796+
False,
797+
self.dummy_management_key,
798+
)
799+
800+
with patch("requests.delete") as mock_delete:
801+
network_resp = mock.Mock()
802+
network_resp.ok = True
803+
mock_delete.return_value = network_resp
804+
client.mgmt.outbound_application.delete_user_tokens(app_id="app123")
805+
806+
mock_delete.assert_called_once()
807+
call_args = mock_delete.call_args
808+
assert call_args[1]["params"]["appId"] == "app123"
809+
assert "userId" not in call_args[1]["params"]
810+
811+
def test_delete_user_tokens_with_user_id_only(self):
812+
client = DescopeClient(
813+
self.dummy_project_id,
814+
self.public_key_dict,
815+
False,
816+
self.dummy_management_key,
817+
)
818+
819+
with patch("requests.delete") as mock_delete:
820+
network_resp = mock.Mock()
821+
network_resp.ok = True
822+
mock_delete.return_value = network_resp
823+
client.mgmt.outbound_application.delete_user_tokens(user_id="user456")
824+
825+
mock_delete.assert_called_once()
826+
call_args = mock_delete.call_args
827+
assert call_args[1]["params"]["userId"] == "user456"
828+
assert "appId" not in call_args[1]["params"]
829+
830+
def test_delete_user_tokens_failure(self):
831+
client = DescopeClient(
832+
self.dummy_project_id,
833+
self.public_key_dict,
834+
False,
835+
self.dummy_management_key,
836+
)
837+
838+
with patch("requests.delete") as mock_delete:
839+
mock_delete.return_value.ok = False
840+
self.assertRaises(
841+
AuthException,
842+
client.mgmt.outbound_application.delete_user_tokens,
843+
"app123",
844+
"user456",
845+
)
846+
847+
def test_delete_token_success(self):
848+
client = DescopeClient(
849+
self.dummy_project_id,
850+
self.public_key_dict,
851+
False,
852+
self.dummy_management_key,
853+
)
854+
855+
with patch("requests.delete") as mock_delete:
856+
network_resp = mock.Mock()
857+
network_resp.ok = True
858+
mock_delete.return_value = network_resp
859+
client.mgmt.outbound_application.delete_token("token123")
860+
861+
mock_delete.assert_called_once()
862+
call_args = mock_delete.call_args
863+
assert call_args[1]["params"]["id"] == "token123"
864+
865+
def test_delete_token_failure(self):
866+
client = DescopeClient(
867+
self.dummy_project_id,
868+
self.public_key_dict,
869+
False,
870+
self.dummy_management_key,
871+
)
872+
873+
with patch("requests.delete") as mock_delete:
874+
mock_delete.return_value.ok = False
875+
self.assertRaises(
876+
AuthException,
877+
client.mgmt.outbound_application.delete_token,
878+
"token123",
879+
)
880+
771881
def test_url_param_to_dict(self):
772882
# Test URLParam to_dict method
773883
param = URLParam("test_name", "test_value")

0 commit comments

Comments
 (0)