1

I’m new in Python. Maybe someone more experienced than me can help me.

I’m trying to delete a user from Okta using an API call. In order to delete a user from Okta you need to do the same DELETE request twice. The first one disables the account and the second one removes it.

I have two functions: one for retrieving the user ID using the email address and then the function to delete the user using the ID.

If I go to Postman and run the API call twice it just works. The first one disables the account and the second deletes it.

I’m trying to accomplish that in Python by doing this:

okta_user = okta.user_search(login)
okta_id = okta_user[0]["id"]
for _ in range(2):
    okta.user_delete(okta_id)

But I get this response:

DEBUG: https://fakeoktaaddress.com:443 "DELETE //api/v1/users/00u7ngjk6u2fkJR7o1d7 HTTP/1.1" 204 0
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/requests/models.py", line 971, in json
    return complexjson.loads(self.text, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./offboard.py", line 51, in <module>
    okta.user_delete(okta_id)
  File "/calls/calls.py", line 151, in user_delete
    logging.info(response.json())
                 ^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/requests/models.py", line 975, in json
    raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

As you can see the first call runs perfectly and returns a 204 but the second time seems to be failing.

My theory is that somehow in the second run, python tries to get the value for okta_id again by running one or both of these functions again:

okta_user = okta.user_search(login)
okta_id = okta_user[0]["id"]

If this is indeed happening, then okta_user = okta.user_search(login) will fail, since you can’t search by email a deactivated user.

Could it be that the information that I’ve retrieved and stored in a variable is deleted after the object from the call that created it in the first place is deleted? What am I doing wrong and how could I work around this?

Thanks!

Tried a tuple instead of a regular variable. Also tried a global variable instead of a reusable.

Alvaro
  • 11
  • 3
  • 1
    What is `type(okta_id`)? – matszwecja Apr 12 '23 at 14:38
  • 2
    I'm not familiar with Okta, but if it requires two delete calls to actually delete something, is it possible that the Python API wrapper abstracts that away for you? – Samwise Apr 12 '23 at 14:46
  • 1
    I have doubts about your theory that `okta_user` and `okta_id` are being recalculated inside the for loop. You could `print(okta_user, okta_id)` inside the loop before calling the delete to confirm. I suspect there is something else happening like what @Samwise is suggesting since you aren't directly hitting the API, but rather using a library that deals with the API calls for you. – JNevill Apr 12 '23 at 14:49
  • @matszwecja it's just – Alvaro Apr 12 '23 at 14:50
  • 1
    i don't think there's a problem with okta_id not being defined after the first call, did you try adding some time before the second call? maybe by adding a little delay it would work – Timuran Bicer Apr 12 '23 at 14:51
  • I'm not finding information online about `user_delete` method in https://pypi.org/project/okta/. What module are you using? – JNevill Apr 12 '23 at 14:54
  • @JNevill, if I print that, I get the json() payload from the api call for `okta_user` and the user id number for `okta_id` @Samwise, I'm not sure. It just seems that when I make the second call, the variable that should contain the user id doesn't has it anymore. @TimuranBicer, If I run the delete call and then try to print the `user_id` I get the same error. Thanks everyone for your insight! – Alvaro Apr 12 '23 at 14:56
  • Do you know for sure that the first delete call succeeds? Is it possible that the log line with the 204 gets written, but then a subsequent step within that first delete call throws the exception? – slothrop Apr 12 '23 at 14:59
  • @JNevill, I've created my own module: (missing the __init__ because it's too long to post here) ``` def user_delete(self, uid): """Deletes the user""" payload = {} headers = self.headers response = requests.delete(self.baseurl+"/users/"+uid, headers=headers, data=payload, timeout=(3, 5)) logging.info(response.json()) if response.status_code != 204: sys.exit( f"okta.user_deactivate call failed with status {response.status_code}") return response.status_code ``` – Alvaro Apr 12 '23 at 14:59
  • @slothrop Yes, the first call succeeds. I can confirm with both the 204 and then checking in Okta that the user has been disabled. – Alvaro Apr 12 '23 at 15:01
  • @Alvaro right, thanks. What I should have asked is: do you know that the whole of the first call to your Python `user_delete` function succeeds - not just the API call? For example, it could be that the first call to the function correctly calls the API, but fails when trying to extract JSON from the response. Especially given that the expected API response for a deletion doesn't have any JSON, it's `204 No Content` (https://developer.okta.com/docs/reference/api/users/#response-parameters-14) – slothrop Apr 12 '23 at 15:14
  • Running the call + a print + call + another print fixed the issue somehow. I still don't fully understand why that works but two consecutive calls don't. ``` print(f"Disabling {okta_id}") okta.user_delete(okta_id) print(f"Deleting {okta_id}") okta.user_delete(okta_id) ``` – Alvaro Apr 13 '23 at 10:02

0 Answers0