I’m trying to integrate the TikTok API into my application using the PKCE (Proof Key for Code Exchange) flow for OAuth 2.0. Despite following the documentation and ensuring that the code_verifier
and code_challenge
are generated and passed correctly, I keep receiving the error:
{
"error": "invalid_request",
"error_description": "Code verifier or code challenge is invalid.",
"log_id": "..."
}
Here’s a summary of my implementation:
- Generating
code_verifier
andcode_challenge
:- The
code_verifier
is a high-entropy random string of 64 characters. - The
code_challenge
is the base64-url-encoded SHA-256 hash of thecode_verifier
.
- The
import secrets
import hashlib
import base64
import requests
from flask import Flask, request, redirect, session
import re
import urllib.parse
app = Flask(__name__)
app.secret_key = secrets.token_urlsafe(64)
client_key = 'your_client_key'
client_secret = 'your_client_secret'
redirect_uri = 'http://127.0.0.1:5000/callback/'
scopes = 'video.upload'
def generate_random_string(length):
characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'
return ''.join(secrets.choice(characters) for _ in range(length))
def generate_code_challenge_pair():
code_verifier = generate_random_string(64) # Generate a code_verifier with 64 characters
sha256_hash = hashlib.sha256(code_verifier.encode('utf-8')).digest()
code_challenge = base64.urlsafe_b64encode(sha256_hash).decode('utf-8').rstrip('=')
return code_verifier, code_challenge
@app.route('/')
def home():
code_verifier, code_challenge = generate_code_challenge_pair()
state = generate_random_string(16)
session['code_verifier'] = code_verifier
session['state'] = state
tiktok_auth_url = (
f'https://www.tiktok.com/v2/auth/authorize/?'
f'client_key={client_key}&scope={scopes}&response_type=code&'
f'redirect_uri={redirect_uri}&state={state}&'
f'code_challenge={code_challenge}&code_challenge_method=S256'
)
return redirect(tiktok_auth_url)
@app.route('/callback/')
def callback():
code = request.args.get('code')
state = request.args.get('state')
if state != session.get('state'):
return 'CSRF verification failed', 400
code_verifier = session.get('code_verifier')
if not code_verifier:
return 'Missing code verifier', 400
decoded_code = urllib.parse.unquote(code)
access_token = get_access_token(decoded_code, code_verifier)
if access_token:
return f'Authentication successful! Access token: {access_token}'
else:
return 'Failed to obtain access token', 400
def get_access_token(code, code_verifier):
url = 'https://open.tiktokapis.com/v2/oauth/token/'
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
payload = {
'client_key': client_key,
'client_secret': client_secret,
'code': code,
'grant_type': 'authorization_code',
'redirect_uri': redirect_uri,
'code_verifier': code_verifier
}
response = requests.post(url, headers=headers, data=payload)
data = response.json()
if 'access_token' in data:
return data['access_token']
return None
if __name__ == '__main__':
app.run(port=5000, debug=True)
-
Redirecting to TikTok for authorization:
- The user is redirected to TikTok’s authorization URL with the necessary parameters, including
code_challenge
andstate
.
- The user is redirected to TikTok’s authorization URL with the necessary parameters, including
-
Handling the callback:
- The authorization code is captured and the state is verified.
- The
code_verifier
is then used to request an access token.
Despite this setup, the response from TikTok’s token endpoint consistently states that the “Code verifier or code challenge is invalid.”
I’ve double-checked the generation and passing of both the code_verifier
and code_challenge
. Here are some example values for reference:
- State:
WBwqf0e8J5NKZukn
- Code:
okYXNycnagbXKg4Alkum9JPnGI-WJsnxENO3NCCHHxVilhKE_XzPHhKnBjgprrMtBai2vQrDu2YIYjm3nzgZv4HgCB4nxffI0zm36W69d5jDpGsh_gfmjR9CG6RG4saRDuFeTTIr0IYnx7fDFNhiAv1KkYorssycwM4VbBNEr_FJUtBiyv7Cw_WU9EylNoa5Vl6H2jFCw4poIMsUwE7JSBm7OgM4DzceqfS5z7_yAP14pOWaT3GHqd0jALys0LNX*1!4549.va
- Code Verifier:
Q6TYgUOmuBlhmw1IgAVSPJQ4t14sf.CdD4f3.ZR2ZLpCumYzIkMD4b7-DFwoffCs
- Payload:
{'client_key': 'sbaw7maxtihzttrwqu', 'client_secret': 'T1J1o0JwyZf3ZehwRqiTnfs7JvRFUYk3', 'code': 'okYXNycnagbXKg4Alkum9JPnGI-WJsnxENO3NCCHHxVilhKE_XzPHhKnBjgprrMtBai2vQrDu2YIYjm3nzgZv4HgCB4nxffI0zm36W69d5jDpGsh_gfmjR9CG6RG4saRDuFeTTIr0IYnx7fDFNhiAv1KkYorssycwM4VbBNEr_FJUtBiyv7Cw_WU9EylNoa5Vl6H2jFCw4poIMsUwE7JSBm7OgM4DzceqfS5z7_yAP14pOWaT3GHqd0jALys0LNX*1!4549.va', 'grant_type': 'authorization_code', 'redirect_uri': 'http://127.0.0.1:5000/callback/', 'code_verifier': 'Q6TYgUOmuBlhmw1IgAVSPJQ4t14sf.CdD4f3.ZR2ZLpCumYzIkMD4b7-DFwoffCs'}
Does anyone have experience with TikTok’s PKCE implementation or see where I might be going wrong? Any help would be greatly appreciated!