Refreshing tokens
Access tokens are short-lived. How you renew depends on how you signed in. There are two cases: the device refresh token, and machine-to-machine (which does not refresh at all).
Refresh-token rotation is opportunistic: there is no fixed rotation or expiry policy. The device path persists a rotated refresh token whenever the server returns one. Always store the newest refresh token you receive.
Case A: device refresh token
If you signed in with the device flow or PKCE and asked for offline_access, you have a refresh token. Exchange it at the token endpoint with grant_type=refresh_token. The native client is public, so there is no client secret in this call.
curl -sS -X POST https://auth.cybersentriq.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=refresh_token" \
--data-urlencode "client_id=YM8rjZukoH5rG2hnhMK82lSMAZYP57WR" \
--data-urlencode "refresh_token=<REFRESH_TOKEN>"
const body = new URLSearchParams({
grant_type: "refresh_token",
client_id: "YM8rjZukoH5rG2hnhMK82lSMAZYP57WR",
refresh_token: storedRefreshToken,
});
const res = await fetch("https://auth.cybersentriq.com/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body,
});
const data = await res.json();
// Persist the rotated refresh token if one came back.
if (data.refresh_token) storedRefreshToken = data.refresh_token;
import requests
res = requests.post(
"https://auth.cybersentriq.com/oauth/token",
data={
"grant_type": "refresh_token",
"client_id": "YM8rjZukoH5rG2hnhMK82lSMAZYP57WR",
"refresh_token": stored_refresh_token,
},
)
data = res.json()
# Persist the rotated refresh token if one came back.
if data.get("refresh_token"):
stored_refresh_token = data["refresh_token"]
using System.Text.Json;
var form = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "refresh_token",
["client_id"] = "YM8rjZukoH5rG2hnhMK82lSMAZYP57WR",
["refresh_token"] = storedRefreshToken,
});
var res = await http.PostAsync("https://auth.cybersentriq.com/oauth/token", form);
using var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
var root = doc.RootElement;
// Persist the rotated refresh token if one came back.
if (root.TryGetProperty("refresh_token", out var rt))
storedRefreshToken = rt.GetString();
form := url.Values{
"grant_type": {"refresh_token"},
"client_id": {"YM8rjZukoH5rG2hnhMK82lSMAZYP57WR"},
"refresh_token": {storedRefreshToken},
}
res, err := http.PostForm("https://auth.cybersentriq.com/oauth/token", form)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
var data struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int `json:"expires_in"`
}
json.NewDecoder(res.Body).Decode(&data)
// Persist the rotated refresh token if one came back.
if data.RefreshToken != "" {
storedRefreshToken = data.RefreshToken
}
The response is a fresh access token, and often a rotated refresh token. Always store the new refresh token if one is returned, and use it next time. If the exchange fails with invalid_grant, the refresh token is spent or revoked; sign in again.
Case B: machine to machine (no refresh token)
The client-credentials grant does not issue a refresh token, and does not need one. When your cached token nears expiry, request a new one exactly as in Service account and M2M authentication. Caching the token and re-requesting on expiry is the whole story.
Next
- Send your token to the API: Endpoints you call.
- Handle a 401 and refresh in context: End-to-end example.