When using "authorizationMode": "PrivateKey", the user_client.forgot_password_generate_one_time_token function fails to invoke /api/v1/users/{userId}/credentials/forgot_password due to a 400 Bad request error. This prevents any usage of that forgot password API by the SDK in this mode.
Affects
All versions of Python 3.
All versions of library that I found, but I didn't test extensively into the past.
But why are these related? There's a subsequent behavior ( I would call this a bug but it might be ingrained at this point) where every request to send_request in the client updates the _default_headers.
So the order of operations is: when trying to fire forgot_password_generate_one_time_token on a fresh client, first get_access_token is called which updates the _default_headers which are then used for the final call to /api/v1/users/{userId}/credentials/forgot_password which doesn't like the headers.
from okta.client import Client as OktaClient
# Begin fill with real values
OKTA_ORG_URL = ""
OKTA_PRIVATE_KEY = ""
OKTA_CLIENT_ID = ""
# End fill
okta_client = OktaClient(
{
"orgUrl": OKTA_ORG_URL,
"authorizationMode": "PrivateKey",
"clientId": OKTA_CLIENT_ID,
"privateKey": OKTA_PRIVATE_KEY,
"scopes": [
"okta.users.manage",
"okta.users.read",
"okta.groups.read",
"okta.groups.manage",
],
}
)
await okta_client.forgot_password_generate_one_time_token("some-non-existent-user@example.com")
The output should be a 404 but instead you get
(None,<okta.api_response.OktaAPIResponse at 0x104ac70a0>, {'message': 'Okta HTTP 400 E0000021 Bad request. Accept and/or Content-Type headers likely do not match supported values.\n'})
Proposed Solutions
I'm sure there are more, but I can think of three long term solutions in decreasing feasibility
order (1=easiest, 3=hardest). Although I'm sure 2 isn't easy.
I also include a workaround that has worked for my purposes.
Long Term
1 - Specify the Headers required by the endpoint
Since there is a requirement for a specific Content-Type, specify that in the headers of forgot_password_generate_one_time_token which are currently {}. This is a one-line fix
to specify Content-Type: JSON for this specific call.
2 - Non-backwards incompatible change to the API
The Okta API could become more permissive on /api/v1/users/{userId}/credentials/forgot_password.
Doing so, I believe, would not break compatibility, just add support for a new Content-Type.
Possibly not as simple as I state, and I haven't looked into all that the endpoint supports.
3 - Stop changing default
This would take significantly more testing/validation as it would affect all calls in the client,
but this does seem like the primary mistake. API calls shouldn't bleed into others, perhaps
outside of setting session information.
Workaround
Until a fix is in place, you can set the custom_headers to apply to all requests by default.
Problem
When using
"authorizationMode": "PrivateKey",
theuser_client.forgot_password_generate_one_time_token
function fails to invoke/api/v1/users/{userId}/credentials/forgot_password
due to a 400 Bad request error. This prevents any usage of that forgot password API by the SDK in this mode.Affects
All versions of Python 3.
All versions of library that I found, but I didn't test extensively into the past.
Technical Details
The
forgot_password_generate_one_time_token
code in the user_client does not specify headers on its request, allowing the default headers and custom headers to mandate what headers are used https://github.com/okta/okta-sdk-python/blob/d896f65ceab0671e64945525a84af6ce389fdd1d/okta/resource_clients/user_client.py#L899-L905The problem is that the endpoint does not respond to
Content-Type: application/x-www-form-urlencoded
, and will return a 400 if used. Unfortunately, theget_access_token
code sets that content type when it requests an access token. https://github.com/okta/okta-sdk-python/blob/d896f65ceab0671e64945525a84af6ce389fdd1d/okta/oauth.py#L61-L72But why are these related? There's a subsequent behavior ( I would call this a bug but it might be ingrained at this point) where every request to
send_request
in the client updates the_default_headers
.https://github.com/okta/okta-sdk-python/blob/d896f65ceab0671e64945525a84af6ce389fdd1d/okta/http_client.py#L68
So the order of operations is: when trying to fire
forgot_password_generate_one_time_token
on a fresh client, firstget_access_token
is called which updates the_default_headers
which are then used for the final call to/api/v1/users/{userId}/credentials/forgot_password
which doesn't like the headers.Steps to Reproduce
Setting up an environment
Inside a python shell
The output should be a 404 but instead you get
Proposed Solutions
I'm sure there are more, but I can think of three long term solutions in decreasing feasibility order (1=easiest, 3=hardest). Although I'm sure 2 isn't easy. I also include a workaround that has worked for my purposes.
Long Term
1 - Specify the Headers required by the endpoint
Since there is a requirement for a specific
Content-Type
, specify that in the headers offorgot_password_generate_one_time_token
which are currently{}
. This is a one-line fix to specifyContent-Type: JSON
for this specific call.2 - Non-backwards incompatible change to the API
The Okta API could become more permissive on
/api/v1/users/{userId}/credentials/forgot_password
. Doing so, I believe, would not break compatibility, just add support for a new Content-Type. Possibly not as simple as I state, and I haven't looked into all that the endpoint supports.3 - Stop changing default
This would take significantly more testing/validation as it would affect all calls in the client, but this does seem like the primary mistake. API calls shouldn't bleed into others, perhaps outside of setting session information.
Workaround
Until a fix is in place, you can set the
custom_headers
to apply to all requests by default.Which yields a "successful" 404: