Skip to main content
Socialift
Guide

TikTok Login Kit: OAuth flow and token lifecycle

TikTok Login Kit is OAuth 2.0. Your app sends the user to https://www.tiktok.com/v2/auth/authorize/ with its client_key, scopes and a state value, receives a code on the registered redirect URI and exchanges it server-side at /v2/oauth/token/. The access token lasts 24 hours and the refresh token 365 days, so schedule a daily refresh.

Updated:

How does TikTok OAuth work?

TikTok uses the authorization code flow. The user approves the requested scopes on TikTok's authorization page, TikTok redirects back with a code, and your server exchanges it — together with the client key and client secret — for an access token, a refresh token and the user's open_id. API calls then send the access token as Authorization: Bearer.

Login Kit exists for Web, Desktop, iOS and Android. The web flow relies on a server that holds the client secret and checks the state value; desktop and mobile flows add PKCE and send a code_verifier during the code exchange.

Scopes decide what the token can do. user.info.basic is added automatically; anything else, such as video.list or video.publish, must be added to your app in the developer portal and covered in app review.

Key terms: client key, client secret, open_id and tokens

client_key
The app's public identifier. Sent in the authorization URL and in token requests.
client_secret
The app's confidential credential. Used only in server-side requests to /v2/oauth/token/ and /v2/oauth/revoke/; never shipped in a browser or mobile binary.
code
The authorization code returned to your redirect URI after the user consents. Exchange it for tokens right away.
access_token
Bearer token for API calls, valid for 24 hours after issuance.
refresh_token
Used to obtain a new access token; valid for 365 days after initial issuance. A refresh response may contain a different refresh token.
open_id
The TikTok user's unique identifier, returned with the token. Use it as the key of the connected account.
state
A random value you create before the redirect and compare on callback to block CSRF.

How to set up TikTok OAuth for a web app

  1. Register redirect URIs

    Add up to 10 redirect URIs in the Login Kit settings. Each must be absolute, start with https, be under 512 characters, be static (no query parameters) and contain no # fragment.

  2. Create and store a state value

    Generate a random string for every login attempt and keep it in the user's session or a short-lived cookie. TikTok's own web sample uses a short-lived cookie too.

  3. Redirect to the authorization page

    Open https://www.tiktok.com/v2/auth/authorize/ with client_key, response_type=code, a comma-separated scope, redirect_uri and state. Add disable_auto_auth=1 to always show the consent page.

  4. Validate the callback

    TikTok returns code, scopes and state, or error and error_description. Reject the request if state does not match, and compare the granted scopes with what you asked for.

  5. Exchange the code on the server

    POST to https://open.tiktokapis.com/v2/oauth/token/ as application/x-www-form-urlencoded with client_key, client_secret, code, grant_type=authorization_code and the same redirect_uri. Desktop and mobile apps also send code_verifier.

  6. Store tokens securely

    Save access_token, refresh_token, both expiry times and open_id, encrypted at rest. TikTok's web guide makes the server explicitly responsible for keeping the client secret and refresh token secure.

Login Kit in Python

Framework-agnostic handlers: session is whatever session storage your web framework provides.

tiktok_login.py
import secrets
from socialift import TikTok

REDIRECT_URI = "https://app.example.com/oauth/tiktok/callback"
SCOPES = ["user.info.basic", "video.list", "video.publish"]

async def begin_login(session: dict) -> str:
    session["tiktok_state"] = secrets.token_urlsafe(32)
    async with TikTok.from_env() as tt:
        return tt.login_url(
            redirect_uri=REDIRECT_URI,
            state=session["tiktok_state"],
            scopes=SCOPES,
        )

async def finish_login(session: dict, code: str, state: str) -> dict:
    expected = session.pop("tiktok_state", "")
    if not expected or not secrets.compare_digest(state, expected):
        raise ValueError("OAuth state mismatch")
    async with TikTok.from_env() as tt:
        bundle = await tt.exchange_code(code, redirect_uri=REDIRECT_URI)
    return {
        "open_id": bundle.raw.get("open_id"),
        "access_token": bundle.access_token,
        "refresh_token": bundle.refresh_token,
        "expires_at": bundle.expires_at,
        "scopes": bundle.scopes,
    }

exchange_code sends a form-urlencoded body, as TikTok requires, and raises ApiError if no access token comes back.

Store the result encrypted. The SDK keeps nothing between calls, so a single client instance can serve many creators.

PKCE: web vs desktop vs mobile

PKCE: web vs desktop vs mobile — Capability matrix by platform
PlatformPKCERedirect URI rules
WebNot part of the Login Kit for Web guide; security relies on the server-side secret and stateAbsolute https, static, no fragment, up to 10 URIs, under 512 characters
DesktopRequired: code_challenge and code_challenge_method=S256 in the URL, code_verifier at exchangehttp or https, localhost or 127.0.0.1 allowed, port required (wildcards allowed), no parameters or fragment
iOS / Androidcode_verifier required at token exchangeBased on the platform details registered for the app (package name, bundle ID)

TikTok's desktop guide computes code_challenge as the hex-encoded SHA-256 of the verifier — not the base64url encoding used in RFC 7636.

PKCE for desktop apps

The verifier must be 43–128 characters long and use only A–Z, a–z, 0–9 and -, ., _, ~.

tiktok_desktop_pkce.py
import hashlib
import secrets
from socialift import TikTok

REDIRECT_URI = "http://127.0.0.1:3455/callback/"

def make_pkce() -> tuple[str, str]:
    verifier = secrets.token_urlsafe(48)                       # 64 characters
    challenge = hashlib.sha256(verifier.encode()).hexdigest()  # hex, as TikTok documents
    return verifier, challenge

async def desktop_login(state: str) -> tuple[str, str]:
    verifier, challenge = make_pkce()
    async with TikTok.from_env() as tt:
        url = tt.login_url(
            redirect_uri=REDIRECT_URI,
            state=state,
            code_challenge=challenge,
            code_challenge_method="S256",
        )
    return url, verifier

async def desktop_callback(code: str, verifier: str):
    async with TikTok.from_env() as tt:
        return await tt.exchange_code(code, redirect_uri=REDIRECT_URI, code_verifier=verifier)

Run the code exchange on your backend: the token request still includes the client secret.

Token lifetimes and what to do about them

Token lifetimes and what to do about them — Capability matrix by platform
SituationLifetimeWhat to do
access_token24 hours after issuanceRefresh before it expires; the exact value is in expires_in
refresh_token365 days after initial issuanceIf the refresh response carries a different refresh token, replace the stored one
Refresh token expirednoSend the user through Login Kit again
User removed your appThe access token is already invalid when authorization.removed arrivesMark the account disconnected and stop calls
/v2/oauth/revoke/ calledYour app's access is revokedDelete the stored tokens

yesno

Refresh daily, revoke on disconnect

tiktok_tokens.py
from datetime import datetime, timedelta, timezone
from socialift import ApiError, TikTok

async def ensure_fresh(account: dict) -> dict:
    left = account["expires_at"] - datetime.now(timezone.utc)
    if left > timedelta(hours=2):
        return account
    async with TikTok.from_env() as tt:
        try:
            bundle = await tt.refresh_token(account["refresh_token"])
        except ApiError:
            account["status"] = "reconnect_required"
            return account
    account.update(
        access_token=bundle.access_token,
        refresh_token=bundle.refresh_token or account["refresh_token"],
        expires_at=bundle.expires_at,
    )
    return account

async def disconnect(account: dict) -> None:
    async with TikTok.from_env() as tt:
        await tt.revoke(account["access_token"])

Run the refresh from a cron job or queue at an interval shorter than 24 hours. Persist the refresh token from every response — TikTok requires you to use the new value when it changes.

The scope parameter is a comma-separated list, and each scope must already be assigned to your app on the TikTok for Developers site. On callback TikTok reports the granted scopes in the scopes parameter — store it and check it before calling an endpoint that needs a specific scope.

When you add scopes later, send existing users through authorization again. With disable_auto_auth=0 TikTok skips the authorization page for valid sessions; with disable_auto_auth=1 it always shows it, which is what you want when the requested scopes change. In the SDK: tt.login_url(..., disable_auto_auth=True).

If a user removes your app in TikTok settings, a registered webhook receives authorization.removed; by then the access token is already invalid. The Socialift SDK verifies these deliveries with verify_tiktok_request and parses them with parse_tiktok_event. The exact redirect and webhook URLs to register with TikTok are shown on the Keys (Kalitlar) page of the Socialift dashboard.

TikTok OAuth security checklist

  • Keep the client secret and refresh tokens on the server only, encrypted at rest.
  • Generate a fresh state for every login and reject callbacks where it does not match.
  • Register exact, static HTTPS redirect URIs; carry context in the state or session, not in query parameters.
  • Use PKCE with S256 in desktop apps and send code_verifier at exchange.
  • Refresh access tokens on a schedule shorter than 24 hours and persist rotated refresh tokens.
  • Revoke tokens with /v2/oauth/revoke/ when a user disconnects inside your product.
  • Log the log_id of failed API calls for TikTok support.

← Back to blog

Frequently asked questions

How long is a TikTok access token valid?

The access token is valid for 24 hours after issuance and the refresh token for 365 days after initial issuance. An integration therefore needs a daily refresh job; once the refresh token expires, the user must authorize again.

Does TikTok return a new refresh token on refresh?

It can. TikTok's documentation says the returned refresh_token may differ from the one you sent, and in that case you must use the new value. Update the stored token after every refresh.

Is PKCE required for TikTok web login?

The Login Kit for Web guide does not use PKCE: the web flow relies on the server-side client secret and the state check. Desktop and mobile apps must send code_verifier during the code exchange; for desktop, code_challenge is a hex-encoded SHA-256.

Why do I get a redirect_uri error?

The redirect_uri in the request must exactly match a value registered in the app settings. For web it has to be an absolute https URL, static (no parameters) and without a # fragment; send the same value again during the code exchange.

How do I revoke a TikTok token?

Send a POST to https://open.tiktokapis.com/v2/oauth/revoke/ with client_key, client_secret and token. In the Socialift SDK that is await tt.revoke(access_token).

Which scope is granted by default?

user.info.basic is added automatically for apps using Login Kit; it returns open_id, avatar and display name. Every other scope has to be added to the app separately.

Sources

  1. TikTok for Developers — Login Kit for Web
  2. TikTok for Developers — Login Kit for Desktop
  3. TikTok for Developers — User access token management
  4. TikTok for Developers — Scopes reference
  5. TikTok for Developers — Webhook events

Related pages