Skip to content

Commit 0e1c4c2

Browse files
committed
Add handling for unexpected successful token response
1 parent 6e3b239 commit 0e1c4c2

File tree

2 files changed

+104
-4
lines changed

2 files changed

+104
-4
lines changed

packages/auth0_api_python/src/auth0_api_python/api_client.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -465,11 +465,24 @@ async def get_access_token_for_connection(self, options: dict[str, Any]) -> dict
465465
response.status_code
466466
)
467467

468-
token_endpoint_response = response.json()
468+
try:
469+
token_endpoint_response = response.json()
470+
except Exception:
471+
raise ApiError("invalid_json", "Token endpoint returned invalid JSON.")
472+
473+
access_token = token_endpoint_response.get("access_token")
474+
if not isinstance(access_token, str) or not access_token:
475+
raise ApiError("invalid_response", "Missing or invalid access_token in response.", 502)
476+
477+
expires_in_raw = token_endpoint_response.get("expires_in", 3600)
478+
try:
479+
expires_in = int(expires_in_raw)
480+
except (TypeError, ValueError):
481+
raise ApiError("invalid_response", "expires_in is not an integer.", 502)
469482

470483
return {
471-
"access_token": token_endpoint_response.get("access_token"),
472-
"expires_at": int(time.time()) + int(token_endpoint_response.get("expires_in", 3600)),
484+
"access_token": access_token,
485+
"expires_at": int(time.time()) + expires_in,
473486
"scope": token_endpoint_response.get("scope", "")
474487
}
475488

packages/auth0_api_python/tests/test_api_client.py

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1819,4 +1819,91 @@ async def test_get_access_token_for_connection_error_text_json_content_type(http
18191819
})
18201820
assert err.value.code == "invalid_request"
18211821
assert err.value.status_code == 400
1822-
assert "bad request" in str(err.value).lower()
1822+
assert "bad request" in str(err.value).lower()
1823+
1824+
@pytest.mark.asyncio
1825+
async def test_get_access_token_for_connection_invalid_json(httpx_mock: HTTPXMock):
1826+
httpx_mock.add_response(
1827+
method="GET",
1828+
url="https://auth0.local/.well-known/openid-configuration",
1829+
json={"token_endpoint": "https://auth0.local/oauth/token"}
1830+
)
1831+
httpx_mock.add_response(
1832+
method="POST",
1833+
url="https://auth0.local/oauth/token",
1834+
status_code=200,
1835+
content="not a json", # Invalid JSON
1836+
headers={"Content-Type": "application/json"}
1837+
)
1838+
options = ApiClientOptions(
1839+
domain="auth0.local",
1840+
audience="my-audience",
1841+
client_id="cid",
1842+
client_secret="csecret",
1843+
)
1844+
api_client = ApiClient(options)
1845+
with pytest.raises(ApiError) as err:
1846+
await api_client.get_access_token_for_connection({
1847+
"connection": "test-conn",
1848+
"access_token": "user-token"
1849+
})
1850+
assert err.value.code == "invalid_json"
1851+
assert "invalid json" in str(err.value).lower()
1852+
1853+
@pytest.mark.asyncio
1854+
async def test_get_access_token_for_connection_invalid_access_token_type(httpx_mock: HTTPXMock):
1855+
httpx_mock.add_response(
1856+
method="GET",
1857+
url="https://auth0.local/.well-known/openid-configuration",
1858+
json={"token_endpoint": "https://auth0.local/oauth/token"}
1859+
)
1860+
httpx_mock.add_response(
1861+
method="POST",
1862+
url="https://auth0.local/oauth/token",
1863+
status_code=200,
1864+
json={"access_token": 12345, "expires_in": 3600} # access_token not a string
1865+
)
1866+
options = ApiClientOptions(
1867+
domain="auth0.local",
1868+
audience="my-audience",
1869+
client_id="cid",
1870+
client_secret="csecret",
1871+
)
1872+
api_client = ApiClient(options)
1873+
with pytest.raises(ApiError) as err:
1874+
await api_client.get_access_token_for_connection({
1875+
"connection": "test-conn",
1876+
"access_token": "user-token"
1877+
})
1878+
assert err.value.code == "invalid_response"
1879+
assert "access_token" in str(err.value).lower()
1880+
assert err.value.status_code == 502
1881+
1882+
@pytest.mark.asyncio
1883+
async def test_get_access_token_for_connection_expires_in_not_integer(httpx_mock: HTTPXMock):
1884+
httpx_mock.add_response(
1885+
method="GET",
1886+
url="https://auth0.local/.well-known/openid-configuration",
1887+
json={"token_endpoint": "https://auth0.local/oauth/token"}
1888+
)
1889+
httpx_mock.add_response(
1890+
method="POST",
1891+
url="https://auth0.local/oauth/token",
1892+
status_code=200,
1893+
json={"access_token": "abc123", "expires_in": "not-an-int"}
1894+
)
1895+
options = ApiClientOptions(
1896+
domain="auth0.local",
1897+
audience="my-audience",
1898+
client_id="cid",
1899+
client_secret="csecret",
1900+
)
1901+
api_client = ApiClient(options)
1902+
with pytest.raises(ApiError) as err:
1903+
await api_client.get_access_token_for_connection({
1904+
"connection": "test-conn",
1905+
"access_token": "user-token"
1906+
})
1907+
assert err.value.code == "invalid_response"
1908+
assert "expires_in" in str(err.value).lower()
1909+
assert err.value.status_code == 502

0 commit comments

Comments
 (0)