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 TypeLifetimeRenewable
Authorization Code10 minutesNo, single-use
Access Token1 hourYes, via refresh token
Refresh Token30 daysNo, 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

Refreshing 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):

  1. API requests will start failing with 401 errors
  2. The refresh token will not be able to recover from this state
  3. You must prompt the user to re-authorize through the full OAuth flow

Summary

ScenarioAction
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 accessHandle gracefully, prompt re-authorization
TikTok session expiresRevoke tokens, require full re-authorization
API returns 401Attempt refresh, then re-authorization if needed