Skip to main content
Sign In with TikTok using authorization code flow through the Despia OAuth bridge. Native opens a secure browser session via oauth://, web uses a standard redirect, and the code-to-token exchange always runs server-side because TikTok’s Client Secret cannot be exposed client-side.
All native capabilities here come from despia-native. No additional native libraries are needed.

Installation


How TikTok OAuth differs from Google

TikTok uses the authorization code flow. This means tokens never arrive in the URL hash. Instead:
  1. TikTok redirects to your callback with ?code=xxx as a query param
  2. Your backend exchanges the code for tokens using your Client Secret
  3. Your backend returns the tokens to the callback page
  4. The callback page fires the deeplink with the tokens
This is more secure than the implicit flow because your Client Secret never leaves your server. It also means you need a backend endpoint, so there is no hash-based shortcut.

How it works


Platform detection


Generating the TikTok OAuth URL

TikTok’s Client Key is public, it is visible in the OAuth URL the user sees. You can generate the OAuth URL client-side. The Client Secret is never needed here. Pass deeplink_scheme through the state parameter so your callback knows how to build the deeplink. TikTok echoes state back unchanged after auth.
Find your deeplink scheme at Despia > Publish > Deeplink. Replace myapp throughout with your actual scheme.

Sign in button


Backend, exchange the authorization code

Your backend receives the code from the callback page, exchanges it with TikTok for tokens, optionally fetches the user profile, then returns the tokens to the client. The Client Secret is only ever used server-side.

The native-callback.html bridge

This page runs inside the secure browser session. TikTok redirects here with ?code=xxx. Unlike Google’s implicit flow, the code arrives as a query param, not a hash fragment, so React Router stripping the hash is not a concern here. A plain HTML file is still recommended to keep it simple and ensure it always runs fresh.
The code exchange happens inside this page via a fetch call to your backend. The page stays open while the exchange happens, then fires the deeplink to close the session.
If you prefer a React component:
The oauth/ prefix in the deeplink is required. myapp://oauth/auth closes the browser session and navigates the WebView to /auth. myapp://auth without it does nothing and the user stays stuck in the browser.

Handling tokens at /auth

After Despia closes the session and navigates to /auth?access_token=xxx, your auth page reads the token and sets the session. The web flow also lands here after exchanging the code.
If /auth is already mounted when the deeplink arrives, your framework updates the URL without remounting. Token-reading logic that only runs on mount has already fired with empty params and will not run again. The fix is framework-specific and covered in the tabs below.
setSession() is a placeholder. Replace with your auth provider’s equivalent: supabase.auth.setSession() for Supabase, your own session management for a custom backend.

Complete cross-platform handler


TikTok Developer Portal setup

1

Create an app

Go to developers.tiktok.com, create an app, and add the Login Kit product.
2

Configure redirect URIs

Under Login Kit settings, add both redirect URIs:
  • https://yourapp.com/auth for the web flow
  • https://yourapp.com/native-callback for the native flow
Both must be registered exactly as they appear in your code.
3

Request scopes

Request at minimum user.info.basic. This provides open_id, display_name, and avatar_url. Additional scopes (user.info.profile, user.info.stats) are optional.
4

Note your credentials

Copy your Client Key (public, used client-side) and Client Secret (private, server-side only). Store the Client Secret in your backend environment variables only. Never expose it client-side.
5

Configure your deeplink scheme

Find your scheme at Despia > Publish > Deeplink and replace myapp in your code with the actual value.

Debugging

Use this section when the native OAuth flow is not working. Start by identifying which stage is broken:

Debug overlay

Add this to your /auth page during development. Remove it before submitting to the App Store or Google Play.

Reading the output

Common failure points

Secure browser session does not open. Log the URL before passing to despia() and confirm it starts with https://www.tiktok.com/v2/auth/authorize/. TikTok redirects to wrong URL. Both https://yourapp.com/auth and https://yourapp.com/native-callback must be registered in the TikTok Developer Portal under Login Kit. The redirect URI in your code must match exactly including the protocol and no trailing slash. Code exchange fails. The redirect_uri sent to your backend must exactly match what was used in the original OAuth URL. A mismatch will cause TikTok to reject the exchange even if the code is valid. Deeplink does not close the browser session. oauth/ must be present, myapp://oauth/auth. Without it Despia does not intercept the deeplink. Find your scheme at Despia > Publish > Deeplink. Tokens arrive but sign-in never completes. The auth page was already mounted when the deeplink arrived. See Handling tokens at /auth for framework-specific fixes. Supabase bad_jwt error. You are creating JWTs manually. Use generateLink() and verifyOtp() instead to generate real Supabase sessions.

Pre-submission checklist

  • App created with Login Kit enabled
  • Redirect URI https://yourapp.com/auth registered
  • Redirect URI https://yourapp.com/native-callback registered
  • user.info.basic scope requested
  • Client Key and Client Secret noted
  • OAuth URL generated client-side with correct Client Key
  • deeplink_scheme passed through state param as uuid|scheme
  • native-callback page calls backend to exchange code, fires deeplink with tokens
  • Deeplink is {scheme}://oauth/{path}, oauth/ prefix present
  • Deeplink scheme matches Despia > Publish > Deeplink
  • /auth token handler re-runs on URL change, not only on initial mount
  • TIKTOK_CLIENT_SECRET stored in environment variables only, never client-side
  • Code exchange uses matching redirect_uri in both the OAuth URL and the token request
  • For Supabase, generateLink() and verifyOtp() used, not manual JWTs
  • Debug overlay removed from /auth page
  • Sign in tested on a physical device
  • Sign in tested on both iOS and Android
  • Error state tested, cancel the TikTok dialog and confirm the app handles it gracefully


FAQ

TikTok uses authorization code flow, which requires exchanging a short-lived code for tokens using your Client Secret. The Client Secret must never be exposed client-side. Supabase handles Google’s exchange internally, which is why the Google flow appears to not need a backend. For TikTok you always need a backend endpoint to do the exchange.
The native-callback page runs inside the secure browser session (Chrome Custom Tabs / ASWebAuthenticationSession). Doing the exchange here means the tokens are ready before the deeplink fires. If you waited until /auth, you would need to carry the raw code through the deeplink instead of the tokens, which is possible but adds an extra round trip after the browser closes.
It signals Despia to close the secure browser session and navigate the WebView to the path that follows. myapp://oauth/auth closes the session and opens /auth. Without oauth/ the deeplink is ignored and the user stays in the browser.
The /auth page was already open when the deeplink arrived. Your framework updated the URL without reloading.Fix per framework:
  • React, add searchParams to your useEffect dependency array
  • Vue, use watch: { '$route.query': { immediate: true, handler } } instead of mounted()
  • Vanilla JS / HTML, call your handler on load and add window.addEventListener('popstate', handler)

Resources

NPM Package

despia-native

OAuth Reference

Generic OAuth protocol docs

TikTok Developer Portal

Configure your TikTok OAuth app