Exception raised when making an OAuth 2.0 API request without necessary scopes is unhelpful
Details
- Python version: 3.11.5
- Okta package version: 2.9.3
- OS: macOS 13.5.1
I have an API client (okta.client.Client) that authenticates with my Okta organization using OAuth 2.0 via an API Service app integration. This Okta app is given the "Group Membership Administrator" admin assignment, and 2 Okta API scopes:
-
okta.groups.manage -
okta.users.read
However, the API client only includes the okta.users.read scope in its configuration. It is also configured to raise exceptions on errors.
When the API client tries to add a user to a group, an exception is raised (as expected). However, that exception doesn't contain enough information to diagnose why the exception was raised. Here is what the stack trace looks like:
Traceback (most recent call last):
File "/Users/noelle/dev/okta-sdk-error-repro/.venv/lib/python3.11/site-packages/okta/http_client.py", line 139, in check_response_for_error
error = OktaAPIError(url, response_details, formatted_response)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/noelle/dev/okta-sdk-error-repro/.venv/lib/python3.11/site-packages/okta/errors/okta_api_error.py", line 7, in __init__
self.error_code = response_body["errorCode"]
~~~~~~~~~~~~~^^^^^^^^^^^^^
TypeError: string indices must be integers, not 'str'
Traceback (most recent call last):
File "/Users/noelle/dev/okta-sdk-error-repro/.venv/lib/python3.11/site-packages/okta/http_client.py", line 139, in check_response_for_error
error = OktaAPIError(url, response_details, formatted_response)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/noelle/dev/okta-sdk-error-repro/.venv/lib/python3.11/site-packages/okta/errors/okta_api_error.py", line 7, in __init__
self.error_code = response_body["errorCode"]
~~~~~~~~~~~~~^^^^^^^^^^^^^
TypeError: string indices must be integers, not 'str'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/Users/noelle/dev/okta-sdk-error-repro/__main__.py", line 20, in <module>
asyncio.run(main())
File "/Users/noelle/.pyenv/versions/3.11.5/lib/python3.11/asyncio/runners.py", line 190, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/Users/noelle/.pyenv/versions/3.11.5/lib/python3.11/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/noelle/.pyenv/versions/3.11.5/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "/Users/noelle/dev/okta-sdk-error-repro/__main__.py", line 16, in main
response, error = await okta_client.add_user_to_group(GROUP_ID, USER_ID)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/noelle/dev/okta-sdk-error-repro/.venv/lib/python3.11/site-packages/okta/resource_clients/group_client.py", line 1262, in add_user_to_group
response, error = await self._request_executor\
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/noelle/dev/okta-sdk-error-repro/.venv/lib/python3.11/site-packages/okta/request_executor.py", line 174, in execute
_, error = self._http_client.check_response_for_error(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/noelle/dev/okta-sdk-error-repro/.venv/lib/python3.11/site-packages/okta/http_client.py", line 148, in check_response_for_error
raise HTTPException(formatted_response)
okta.exceptions.exceptions.HTTPException
The API response from Okta has an empty body. The actual details for what went wrong are contained in the WWW-Authenticate HTTP header of the response:
Bearer authorization_uri="http://dev-22706224.okta.com/oauth2/v1/authorize", realm="http://dev-22706224.okta.com", scope="okta.groups.manage", error="insufficient_scope", error_description="The access token provided does not contain the required scopes.", resource="/api/v1/groups/00ga2wa4gsRli77iu5d7/users/00uawnm71r7bLPaPE5d7"
The SDK doesn't seem to be aware of this potential source of errors, leaving it to the developer to dig into the responses themself.
Repro
Reproduction details
okta.yaml
okta:
client:
raiseException: true
connectionTimeout: 30 # seconds
orgUrl: "https://dev-22706224.okta.com"
authorizationMode: "PrivateKey"
clientId: "0oab0pj9tsZzD9lqq5d7"
scopes:
# - okta.groups.manage # commented out to produce error
- okta.users.read
privateKey: |
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
requestTimeout: 0 # seconds
rateLimit:
maxRetries: 4
logging:
enabled: true
logLevel: INFO
__main__.py
import asyncio
from okta.client import Client as OktaClient
USER_ID = "foobar"
GROUP_ID = "foobar"
okta_client = OktaClient()
async def main():
response, error = await okta_client.add_user_to_group(GROUP_ID, USER_ID)
return response
if __name__ == "__main__":
asyncio.run(main())
Steps
- Configure a new Okta application as an API service with Public key / Private key client authentication, add the
okta.groups.manage,okta.users.readOkta API scopes, and add a "Group Membership Administrator" admin assignment to it. - Copy
okta.yamland__main__.pyinto a new folder - Fill in real
privateKeyinokta.yaml - Run
pip install okta==2.9.3 - Run
python .