OAuth Integration
Token Lifecycle
Understanding OAuth token expiration, refresh flows, and session management.
Proper token lifecycle management is critical for a seamless user experience. This guide covers how tokens expire, how to refresh them, and how to handle edge cases.
Token Expiration Overview
| Token Type | Lifetime | Renewable |
|---|---|---|
| Authorization Code | 10 minutes | No, single-use |
| Access Token | 1 hour | Yes, via refresh token |
| Refresh Token | 30 days | No, user must re-authorize |
Access Tokens
Access tokens are short-lived (1 hour) and used for API authentication via the x-oauth-token header.
When an access token expires, use the refresh token to obtain a new one without user interaction.
Refresh Tokens
Refresh tokens are longer-lived (30 days) and used exclusively to obtain new access tokens. When a refresh token expires, the user must complete the full authorization flow again.
Token Refresh Flow
sequenceDiagram
participant App as Your App
participant API as Euler Stream API
App->>API: 1. API request with expired access token
API->>App: 2. 401 Unauthorized
App->>API: 3. POST /tiktok/oauth/token (refresh_token grant)
API->>App: 4. New access_token + refresh_token
App->>API: 5. Retry original request
API->>App: 6. SuccessRefreshing Access Tokens
interface TokenResponse {
access_token: string;
refresh_token: string;
token_type: 'Bearer';
expires_in: number;
refresh_expires_in: number;
scopes: string[];
}
async function refreshAccessToken(refreshToken: string): Promise<TokenResponse> {
const response = await fetch('https://tiktok.eulerstream.com/tiktok/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: process.env.OAUTH_CLIENT_ID!,
client_secret: process.env.OAUTH_CLIENT_SECRET!,
}),
});
const result = await response.json();
if (result.code !== 200 || !result.data) {
throw new Error(result.error?.error_description || 'Token refresh failed');
}
return result.data;
}
Important: A successful refresh returns both a new access token AND a new refresh token. Always store the new refresh token — the old one may be invalidated.
Proactive Token Refresh
Don't wait for a 401 error. Refresh tokens proactively before they expire:
interface StoredTokens {
accessToken: string;
refreshToken: string;
accessTokenExpiresAt: Date;
refreshTokenExpiresAt: Date;
}
class TokenManager {
private tokens: StoredTokens;
private refreshPromise: Promise<void> | null = null;
constructor(tokens: StoredTokens) {
this.tokens = tokens;
}
async getValidAccessToken(): Promise<string> {
// Check if access token expires within 5 minutes
const fiveMinutesFromNow = new Date(Date.now() + 5 * 60 * 1000);
if (this.tokens.accessTokenExpiresAt < fiveMinutesFromNow) {
await this.refreshTokens();
}
return this.tokens.accessToken;
}
private async refreshTokens(): Promise<void> {
// Prevent concurrent refresh attempts
if (this.refreshPromise) {
return this.refreshPromise;
}
this.refreshPromise = this.doRefresh();
try {
await this.refreshPromise;
} finally {
this.refreshPromise = null;
}
}
private async doRefresh(): Promise<void> {
// Check if refresh token is expired
if (this.tokens.refreshTokenExpiresAt < new Date()) {
throw new TokenExpiredError('Refresh token expired. User must re-authorize.');
}
const response = await refreshAccessToken(this.tokens.refreshToken);
this.tokens = {
accessToken: response.access_token,
refreshToken: response.refresh_token,
accessTokenExpiresAt: new Date(Date.now() + response.expires_in * 1000),
refreshTokenExpiresAt: new Date(Date.now() + response.refresh_expires_in * 1000),
};
// Persist updated tokens to your database
await this.saveTokens(this.tokens);
}
}
Handling the 30-Day Re-Authorization
When the refresh token expires after 30 days, users must complete the full OAuth flow again. Handle this gracefully:
class TokenExpiredError extends Error {
constructor(message: string) {
super(message);
this.name = 'TokenExpiredError';
}
}
async function makeAuthenticatedRequest(
endpoint: string,
tokenManager: TokenManager
): Promise<Response> {
try {
const accessToken = await tokenManager.getValidAccessToken();
const response = await fetch(`https://tiktok.eulerstream.com${endpoint}`, {
headers: {
'x-oauth-token': accessToken,
},
});
if (response.status === 401) {
// Token was invalidated server-side
throw new TokenExpiredError('Token invalid. User must re-authorize.');
}
return response;
} catch (error) {
if (error instanceof TokenExpiredError) {
// Redirect user to re-authorize
// Clear stored tokens
// Show appropriate UI message
await handleReauthorizationRequired();
}
throw error;
}
}
Token Revocation
Users can revoke their tokens at any time through the consent management page. You can also programmatically revoke tokens:
async function revokeToken(token: string, tokenType: 'access_token' | 'refresh_token') {
const response = await fetch('https://tiktok.eulerstream.com/tiktok/oauth/revoke', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
token: token,
token_type_hint: tokenType,
client_id: process.env.OAUTH_CLIENT_ID!,
client_secret: process.env.OAUTH_CLIENT_SECRET!,
}),
});
// Per RFC 7009, revoke always returns 200 even if the token was already invalid
return response.ok;
}
When to Revoke Tokens
- User disconnects their TikTok account in your app
- User deletes their account from your platform
- Security incident or suspicious activity detected
- User requests data deletion (GDPR, etc.)
Underlying TikTok Session Expiration
The OAuth tokens are backed by the user's TikTok session obtained during QR code authentication. If the underlying TikTok session expires or is invalidated (e.g., user changes password, logs out everywhere):
- API requests will start failing with 401 errors
- The refresh token will not be able to recover from this state
- You must prompt the user to re-authorize through the full OAuth flow
Summary
| Scenario | Action |
|---|---|
| Access token expires (1 hour) | Use refresh token to get new access token |
| Refresh token expires (30 days) | User must re-authorize via full OAuth flow |
| User revokes access | Handle gracefully, prompt re-authorization |
| TikTok session expires | Revoke tokens, require full re-authorization |
| API returns 401 | Attempt refresh, then re-authorization if needed |