Help

The Community will be undergoing maintenance from Friday February 21 - Friday, February 28 and will be "read only" during this time. To learn more, check out our Announcements blog post.

Refresh Token logic intermittently fails

267 0
cancel
Showing results for 
Search instead for 
Did you mean: 
kaustav_gumloop
4 - Data Explorer
4 - Data Explorer

Refresh Token logic intermittently fails

We're experiencing intermittent issues with the OAuth refresh token flow. While our initial token fetch consistently works (200 response from `/oauth2/v1/token`), some users encounter a 400 error during token refresh with:

{
"error": "invalid_grant",
"error_description": "Invalid token."
}

The peculiar aspect is that this only affects some users while working perfectly for others. The initial token exchange works flawlessly in all cases.

I've compared our implementation with the official Airtable example repo and can't seem to find any breaking differences.

Has anyone encountered similar issues or can suggest specific areas to investigate? We're particularly interested in understanding what could cause this intermittent behavior.

Technical details of our implementation are available if needed.

def refresh_airtable_token(token_type, refresh_token):
"""
Refresh the Airtable API token.
"""

token_url = "https://airtable.com/oauth2/v1/token"

client_id = OAUTH_CREDENTIALS["airtable_client_id"]
client_secret = OAUTH_CREDENTIALS["airtable_client_secret"]

# Concatenate client_id and client_secret with a colon
credentials = f'{client_id}:{client_secret}'

# Encode the credentials in base64 format
encoded_credentials = base64.b64encode(credentials.encode()).decode()

headers = {
'Authorization': f'Basic {encoded_credentials}',
'Content-Type': 'application/x-www-form-urlencoded'
}

body = {
'grant_type': 'refresh_token',
'refresh_token': refresh_token,
'scope': 'data.records:read data.records:write data.recordComments:read data.recordComments:write schema.bases:read schema.bases:write user.email:read webhook:manage',
'client_id': client_id
}

response = requests.post(token_url, headers=headers, data=body)
token_data = response.json()

token_data['expires_at'] = get_token_expiry_dt(token_data)

if 'access_token' not in token_data:
raise ValueError(
f"Failed to refresh token. Error: {token_data.get('error_description')}")

return token_data
 
def get_token_expiry_dt(token_data):
# 'expires_in' not required to be returned by the OAuth 2.0 protocol. If it's not returned, just make it refresh next time too
expires_in = token_data.get('expires_in') or 0

# Calculate the expiration datetime -- 10% time buffer instituted for expire time#
current_time = get_datetime_ts()
expires_at = current_time + timedelta(seconds=expires_in * 0.9)

# Modify the json_response to include 'expires_at'
# Represent it in ISO8601 UTC format
return expires_at.isoformat()

 

0 Replies 0