Handle token refresh
Keep a session alive by refreshing the CI HUB and DAM tokens, and recover when one expires.
Using the client library?
TokenManager implements this entire guide: proactive refresh of the CI HUB session, reactive retry on expiry, refresh-token rotation, and the DAM try-then-fallback. See the client library page. What follows documents the wire contract underneath, for other languages or custom implementations.
A connected session holds two kinds of token, each with its own lifetime: the CI HUB access token from token exchange, and the DAM connection token from a DAM login. Both are renewed through GET /auth/refreshToken; the token you send in provider-authorization decides which session is renewed.
Lifetimes
- CI HUB access token. Expires about an hour after exchange. Renew it with the CI HUB refresh token returned alongside it. When the refresh token itself expires, run the token exchange again.
- DAM connection token. Lifetime and refresh model are provider-dependent. Some providers issue a refresh token; others force a fresh login. See the refresh support matrix.
The refresh response returns a new access_token and refresh_token but no expires_in. Track the access token's lifetime from the expires_in returned by the original exchange, or from the token's exp claim.
Detect expiry from the error
You do not have to track expiry to handle it. An expired token surfaces as a specific error code, so a reactive path works: catch the code and refresh.
| Code | HTTP | What expired | Recover by |
|---|---|---|---|
cihub-access-token-invalid | 401 | CI HUB access token | Refresh the CI HUB session. If that fails, re-exchange. |
cihub-refresh-token-invalid | 401 | CI HUB refresh token | Run the token exchange again. |
provider-access-token-invalid | 403 | DAM connection token | Refresh the DAM token, or start a fresh DAM login. |
Refresh the CI HUB session
Send the CI HUB refresh token in provider-authorization. The call returns a new access token and a new refresh token; store both.
You can refresh either way: pre-emptively before the hour is up, or reactively on the first cihub-access-token-invalid. The reactive wrapper below refreshes once and retries; pre-emptive refresh follows the same call on a timer keyed off expires_in.
const BASE_URL = 'https://stage.ci-hub.com/api/v1'
async function withFreshToken(call: (accessToken: string) => Promise<Response>) {
const response = await call(session.accessToken)
if (response.status !== 401) return response
// Only an expired access token is refreshable here; pass other 401s through.
const { error } = await response.clone().json()
if (error?.code !== 'cihub-access-token-invalid') return response
const refresh = await fetch(`${BASE_URL}/auth/refreshToken`, {
headers: {
Authorization: `Bearer ${session.accessToken}`,
'provider-authorization': `Bearer ${session.refreshToken}`,
},
})
if (!refresh.ok) {
// Refresh token is gone too. Re-run the token exchange.
throw new Error('CI HUB session expired; re-exchange required')
}
const { access_token, refresh_token } = await refresh.json()
session.accessToken = access_token
session.refreshToken = refresh_token // rotate; the previous refresh token is spent
return call(session.accessToken)
}Refresh the DAM connection
The DAM connection token expires on the provider's schedule. Renew it through the same endpoint, with the DAM refresh token in provider-authorization. Handle every provider with one path: try the refresh, and fall back to a fresh DAM login when it fails. A 404 means the provider has no refresh path, so treat it (and any other failure) as the signal to log in again. Some providers return only a new access_token; when refresh_token is absent, keep the prior one. The Token refresh reference covers the request and the try-then-fallback pattern.
Storing tokens
The partner platform stores both tokens. CI HUB does not persist DAM connection tokens server-side, so a dropped token means a fresh login, not a server lookup. Refresh rotates the CI HUB refresh token on every call, so always overwrite the stored pair with the values from the latest response. Keep tokens out of logs and analytics.