Stuck on a persistent 401 error that only happens after my access token expires and I attempt to refresh it. I am building a simple Python script to pull interaction data from CXone, and while the initial OAuth token request works fine, the refresh flow fails immediately after the token’s TTL hits.
Context:
I am using the requests library in Python. My setup involves storing the access_token, refresh_token, and expires_in timestamp. When a new API call is made, I check if the current time is past the expiration time. If so, I call the /oauth/token endpoint with grant_type=refresh_token.
Here is the relevant snippet for the refresh logic:
import requests
import time
def get_new_token(client_id, client_secret, refresh_token, org_id):
url = f"https://api.mynice.com/oauth/token"
headers = {"Content-Type": "application/x-www-form-urlencoded"}
data = {
"grant_type": "refresh_token",
"refresh_token": refresh_token,
"client_id": client_id,
"client_secret": client_secret,
"org_id": org_id
}
response = requests.post(url, headers=headers, data=data)
return response.json()
When this executes, I get a 401 Unauthorized. The response body is minimal:
{
"error": "invalid_grant",
"error_description": "Token has expired or is invalid"
}
I have verified that the client_id and client_secret are correct. I also checked the refresh token itself in the CXone admin UI, and it is still active and not revoked. However, I noticed that my local server time is exactly 3 minutes ahead of the CXone server time (I checked this by comparing the date header in the initial token response).
I suspect this clock skew is causing the CXone server to reject my refresh request because it thinks the token is already expired or the request timestamp is invalid.
Question:
How should I handle this clock skew in my Python code? Should I be sending an aud or iat claim manually? Or is there a specific way to adjust the expiration logic to account for server time differences? I want to avoid hardcoding a time offset if possible. Any examples of robust token refresh logic that handles this would be appreciated.