Token lifecycle
How long a token lives, how to cache and refresh it, and what happens on rotation and revocation.
Lifetime
An access token is valid for exactly one hour. The token response says so:
"expires_in": 3600. There is no refresh token, because the client credentials
grant does not issue one. To get a new token, repeat the token request with the
same client ID and client secret.
The pattern
- Request a token once and keep it in memory with its expiry time.
- Reuse it until it is about 60 seconds from expiry. Then request a new one.
- If a call made with the token returns
401, discard the token, request a new one, and retry once. Do not retry in a loop. - Never request a token for every call.
Token requests are throttled
The token endpoint allows 10 requests per minute for each client ID, and 30
requests per minute for each IP address. Failed attempts count too. Above the
limit it returns 429. See Errors.
Cache and refresh
These samples keep one token per process and request a new one 60 seconds before
it expires. They do not call any business endpoint, because none accepts the
token yet. Call get_token() wherever your code needs a token.
import os
import threading
import time
import requests
TOKEN_URL = "https://api.flavours.sa/dev/oauth/token"
REFRESH_MARGIN = 60 # seconds
_lock = threading.Lock()
_token = None
_expires_at = 0.0
def _request_token():
response = requests.post(
TOKEN_URL,
data={
"grant_type": "client_credentials",
"client_id": os.environ["FLAVOURS_CLIENT_ID"],
"client_secret": os.environ["FLAVOURS_CLIENT_SECRET"],
},
timeout=10,
)
response.raise_for_status()
return response.json()
def get_token(force=False):
"""Return a valid access token, requesting a new one when needed."""
global _token, _expires_at
with _lock:
if force or _token is None or time.time() >= _expires_at - REFRESH_MARGIN:
body = _request_token()
_token = body["access_token"]
_expires_at = time.time() + body["expires_in"]
return _tokenOn a 401 from an endpoint that accepts the token, call get_token(force=True)
(Python) or getToken({ force: true }) (JavaScript), then retry the call once.
Rotation
Rotating the secret issues a new client secret for the same client ID.
- The old secret stops working immediately.
- Every access token already issued to the integration is revoked immediately, even if it has not expired.
- Tokens of other integrations are not affected.
- The new secret is shown once. Rotating needs the account password, unless it was confirmed within the last 10 minutes.
Update the stored secret in your system, then request a new token.
Revocation
Revoking permanently disables the integration. The client ID and secret stop working. A revoked integration cannot be rotated or edited, and it cannot be reactivated. To reconnect, create a new integration.
What your client sees
After a rotation, the old secret is rejected. After a revocation, every secret is rejected. In both cases the token endpoint answers a token request with the same response as for any wrong credential:
{
"error": "invalid_client",
"error_description": "Client authentication failed"
}This is HTTP 401. The response does not say whether the secret is wrong, was
rotated, or the integration was revoked. Check in the Flavours web app:
the integration list shows each integration as Active or Revoked.
If your system receives invalid_client after it has worked before, stop
retrying, alert an operator, and check whether the secret was rotated or the
integration revoked. Retrying only consumes the rate limit.