TikTok - Authentication & Database structure
Summary of Changes: (The purpose of this table is to keep traceability and Product team to highlight the things that were changed into the scope, based on comments or discussions)
Date | Version | Created/Updated | Comment |
---|---|---|---|
14/05/2024 | 1.1 | Hristiyan | TikTok has new API version and updates. The scope was also updated to the latest changes on TikTok. Everything that is not tagged with v1.1 remains the same. |
<v1.1>
Purpose of this document is to provide information on the TikTok authentication requirements & Database structure.
Sandbox account:
End points:
Auth: https://auth-sandbox.tiktok-shops.com
All others APIs: https://open-api-sandbox.tiktokglobalshop.com
Please note that we need to add the end points to integration in order to switch between sandbox and prod account
Credentials: TikTok sandbox shop: https://seller-sandbox.tiktok.com/uk/homepage
Username: sandbox_uklc_1ujluezusia@shop.tiktok.com Pass: 98dAQAiG8N3
Authentication
Docs:
First, we need to get an authorization code, using this link:
https://auth.tiktok-shops.com/oauth/authorize?app_key=123abc&state=123 which we have to generate based on the details we have in Integration Credentials and sent to the Account TikTok
> Client email
.
From there once the link is followed we need to approve the authorization and in the url will receive the auth_code using callbacks. We want to have a button in Account TikTok
table which will be called Start TikTok authorisation
. Upon pressing this button the user should receive the link in the email that was filled. After the button is pressed, we should raise the flag of Account TikTok1
> OAuth Began
= Yes
Into this link, we need to replace app_key and state with our app_key & our state code. The key will be in the Integration Credentials
table
Example:
appKey = '123abc'//your key
state = '123abs'//your origin state
//expect url below, trim the space
https://auth.tiktok-shops.com/oauth/authorize?app_key=key&state=123abs
After successful authorization approval, the seller will be redirected to the Redirect URL we provided in ourapp. This URL will contain a temporary auth_code
as a query parameter. This code will expiree in 30 min and can only be used once.
{redirect_url}?code=FeBoANmHP3yqdoUI9fZOCw&state={state}
In the generated link after the redirect we will get the field Auth_Code which is required for the integration(in order to get the Access token)
Once we get the authorization code, we need to “exchange“ it for an access token & refresh token:
1. Request access token and refresh token
To request access and refresh tokens we need to ensure we have a valid auth_code
before requesting
API Call: GET **auth.tiktok-shops.com/api/v2/token/get
Docs: https://partner.tiktokshop.com/docv2/page/6632a7a926b40c02d97de61b#Back To Top
Request Parameters:
TikTok Field | Description | Type | Required | Hemi Field | Notes |
---|---|---|---|---|---|
app_key | string | Yes | N/A | App key in Partner Center app page. |
This will be available in the Integration Credentials
table
|
| app_secret | Your private app secret, please keep it as a secret. | string | Yes | N/A | App secret in Partner Center app page
This will be available in the Integration Credentials
|
| auth_code | The auth_code we obtained in the first step | string | Yes | N/A | |
| grant_type | The way we grant token. Only "authorized_code" is accepted. | string | Yes | authorized_code | |
If all the parameters entered in the GET request are valid, the response will contain the following parameters:
Parameter | Type | Description | Sample |
---|---|---|---|
access_token |
string | User access token needed to make calls to TikTok Shop Open API endpoints | TTP_RLM6CIADWF606TZGFO5XGA |
access_token_expire_in |
Unix timestamp | Expiration timestamp for access token, with default expiration time set to seven days. The Unix timestamp represents the date and time the access token will expire. | 1630401330 |
refresh_token |
string | A token to refresh the access token | TTP_C2XWDN63ON-FOHJSMR0WSG |
refresh_token_expire_in |
Unix timestamp | Expiration timestamp for refresh token. The Unix timestamp represents the date and time the refresh token will expire. | 1630401510 |
open_id |
string | The ID to help you identify a TikTok Shop seller in your platform | ephr6QAAAADhos3OBMztFEwRCWQGzDmfXm_7O2OTJyaYKA15pIaiEg |
seller_name |
string | The name of the seller you are authorizing for your app | Test Name |
seller_base_region |
string | The region where the seller is based | US |
user_type |
int | Type of user, with possible values: 0: Seller 1: Creator | 1 |
request_id |
string | ID to track the API request | 2022080809462301024509910319695C45 |
Example Response:
{
"code":0,
"message":"success",
"data":{
"access_token":"TTP_Fw8rBwAAAAAkW03FYd09DG-9INtpw361hWthei8S3fHX8iPJ5AUv99fLSCYD9-UucaqxTgNRzKZxi5-tfFMtdWqglEt5_iCk",
"access_token_expire_in":1660556783,
"refresh_token":"TTP_NTUxZTNhYTQ2ZDk2YmRmZWNmYWY2YWY2YzkxNGYwNjQ3YjkzYTllYjA0YmNlMw",
"refresh_token_expire_in":1691487031,
"open_id":"7010736057180325637",
"seller_name":"Jjj test shop",
"seller_base_region":"ID",
"user_type":0
},
"request_id":"2022080809462301024509910319695C45"
}
Mapping:
TikTok Field | Description | Type | Required | Hemi Field | Notes | |
---|---|---|---|---|---|---|
code |
N/A | |||||
message |
N/A | |||||
data |
||||||
access_token |
Token for you access data of tt seller | string | N/A | To be stored in cache | ||
access_token_expire_in |
Expire date for your at. Initial expire duration is 7 days | Unix timestamp | N/A | |||
refresh_token |
Token to refresh your access token | string | To be stored in Hemi but not visible in the UI | If the access token is expired, we use the refresh token to get a new access token and its expiration date. | ||
refresh_token_expire_in |
Unix timestamp | Account TikTok > Refresh Token Expiration Date |
We should display the date in human readable format into the UI | |||
open_id |
The unique identity of the tts seller in this app, which is not equal to shop_id. Please use our shop api to obtain your shop id: Shop API | string | N/A | Equal to Shop ID | ||
seller_name |
The name of the seller who authorize you app | string | N/A | |||
request_id |
ID to track the request | string | N/A |
Please note the access will be stored into the cache and the integration will need to check and refresh it when need. refresh token will be stored into the account but not visible in the UI) Once the refresh token is expired we also need to receive some sort of notification or error message (will not receive notifications at this moment). Once we have generated the access and the refresh token, we will need to regenerate the Access Token using the following:
2. Refresh Access Token
API Call: GET auth.tiktok-shops.com/api/v2/token/refresh
TikTok suggests refreshing the access_token
every 7 days using a refresh_token
however, we want to refresh the access token on daily basis. Failure to refresh before its expiration will result in authentication failure when attempting to request data through TikTok Shop APIs.
The refresh_token_expire_in
is equivalent to the authorization duration provided when the seller initially authorized app access.
The access_token
remains valid until the refresh_expire_date
, which is returned in the response.
Request Parameters:
TikTok Field | Description | Type | Required | Hemi Field | Notes |
---|---|---|---|---|---|
app_key | string | Yes | |||
app_secret | Your private app secret, please keep it as a secret. | string | Yes | ||
refresh_token | The code you obtain in the last step | string | Yes | The refresh token is from the response of "Get AccessToken&RefreshToken" API ( see the step above “1“) | |
grant_type | The way you grant token. only "refresh_token" is accepted. | string | Yes | Fixed value “refresh_token“ |
Example Response:
{
"code":0,
"message":"success",
"data":{
"access_token":"TTP_Fw8rBwAAAAAkW03FYd09DG-9INtpw361hWthei8S3fHX8iPJ5AUv99fLSCYD9-UucaqxTgNRzKZxi5-tfFMtdWqglEt5_iCk",
"access_token_expire_in":1660556783,
"refresh_token":"TTP_NTUxZTNhYTQ2ZDk2YmRmZWNmYWY2YWY2YzkxNGYwNjQ3YjkzYTllYjA0YmNlMw",
"refresh_token_expire_in":1691487031,
"open_id":"7010736057180325637",
"seller_name":"Jjj test shop",
"seller_base_region":"ID",
"user_type":0
},
"request_id":"2022080809462301024509910319695C45"
}
Mapping:
TikTok Field | Description | Type | Hemi Field | Notes | |
---|---|---|---|---|---|
code |
|||||
message |
|||||
data |
|||||
access_token |
Token for access data of tt seller | string | To be stored in cache | ||
access_token_expire_in |
Expire date for your at. Initial expire duration is 7 days | Unix timestamp | N/A | ||
refresh_token |
Token to refresh your access token | string | N/A | ||
refresh_token_expire_in |
After 3.11 : Final Expired date of your authorization. The duration would be extended to 365d. | Unix timestamp | Account TikTok > Refresh Token Expiration Date |
||
open_id |
The unique identity of the tts seller in this app, which is not equal to shop_id. Please use our shop api to obtain your shop id: Shop API | string | N/A | ||
seller_name |
The name of the seller who authorize you app | string | N/A | ||
request_id |
ID to track the request | string | N/A |
Sign generation:
https://partner.tiktokshop.com/docv2/page/6632a7c850b2bd02d91d21b4
When calling a TikTok Shop API, a signature must be included to properly authenticate our request. All requests that do not include a signature, or have an invalid signature, will be denied access.
TikTok Shop APIs use HMAC-SHA256 as the default algorithm for generating signatures.
HS256 (HMAC with SHA-256) is a symmetric algorithm, meaning there is only one private key that is shared between the two parties. Since the same key is used to both generate and validate the signature, care must be taken to ensure that the key is not compromised.
When TikTok Shop receives an authenticated request, it recreates the signature using the authentication information contained in the request. If the signatures match, the service processes the request. Otherwise, it rejects the request.
Get Authorised Shops (new call)
****In cross-border scenarios, sellers may have multiple shops. Not all of these shops may be authorized by the developer for API access (authorized to be managed by apps or solutions in the TikTok Shop App Marketplace). Use this API to check which shops are authorized to be serviced by TikTok Shop APIs. This should be automatically triggered after successfull authorisation (after we have successfully stored access token for the given account)
API Call : GET /authorization/202309/shops
Api Docs : https://partner.tiktokshop.com/docv2/page/6507ead7b99d5302be949ba9?external_id=6507ead7b99d5302be949ba9#Back To Top
Request sample :
https://open-api.tiktokglobalshop.com/authorization/202309/shops?app_key=123abc&sign=5361235029d141222525e303d742f9e38aea052d10896d3197ab9d6233730b8c×tamp=1625484268
Sample Response :
{
"code": 0,
"data": {
"shops": [
{
"cipher": "GCP_XF90igAAAABh00qsWgtvOiGFNqyubMt3",
"code": "CNGBCBA4LLU8",
"id": "7000714532876273420",
"name": "Maomao beauty shop",
"region": "GB",
"seller_type": "CROSS_BORDER"
}
]
},
"message": "Success",
"request_id": "202203070749000101890810281E8C70B7"
}
Response mapping :
TikTok Field | Description | Type | Hemi Field | Comment | ||
---|---|---|---|---|---|---|
code |
The success or failure status code returned in API response. | int | N/A | |||
data |
Specific return information | object | N/A | |||
shops |
Seller uses their TikTok Shop seller account to authorize developer. The list of object shows all shops authorized by a TikTok Shop seller to developer. | []object | N/A | |||
cipher |
Use this property to pass shop information in requesting the API. Failure in passing the correct value when requesting the API for cross-border shops will return incorrect response. | |||||
Required for cross-border shops, and optional for local shops. | string | We don’t want to display the cipher in the UI. We want to store this in the database (but not visible for the user) as it is only needed for some API calls. | We have to make sure we are keeping the correct cipher for the correct region (if more than one regions are available) | |||
code |
TikTok Shop code. | string | N/A | |||
id |
TikTok Shop Id. | string | N/A | |||
name |
TikTok Shop name. | string | N/A | |||
region |
Region of the shop. | string | N/AC | We want to map this with Account > Country so we know which shop is it for. (if there are more than one regions on the TikTok seller account) |
||
seller_type |
Indicate the seller is "CROSS_BORDER" or "LOCAL". | string | N/A | |||
message |
||||||
request_id |
Database Structure
Here is a diagram showing the newly created tables for the integration and their relation to the existing ones.
Integration Credentials - here we store our application ids. Please note this table is not visible in the UI and the details are the same for all instances.
Field Name | Type | Required | Comment | Default Value |
---|---|---|---|---|
Integration ID | Textbox | Yes | Id combining all keys for the integration | |
Secret Key | Textbox | Yes | The actual key | |
Secret Name | Textbox | Yes | The name of the key (api_key, api_secret_key) |
Also we need to create a new record for TikTok in Integration table.
Account TikTok - here we store the required details for the integration. Slave table of Account table used for the account setup:
Field Name | Type | Required | Comment | Default Value |
---|---|---|---|---|
Client Email | varchar | Yes | This is mainly required for the authentication process. Where we will receive the link for the Authentication | |
OAuth Began | tinyint/checkbox | No | This indicates whether the authentication process has started or not. It will be used internally by the integration in order to send the email only once, not every time a cron starts. | No |
Shop ID | varchar | Yes | The actual key | |
Integration | int | Yes | Dropdown list of all TikTok integrations. (Integration + Integration Credentials should be saved first in order to have options in the dropdown.) | |
Start TikTok authorisation | button | This should be a button that will generate a link and send it to the email so the user can install the app in TikTok. After click on the button we want to have toast notification with the result. | ||
Refresh Token Expiration Date | varchar | No | We will store the date in human readable format | |
Default Courier | dropdown | No | Enumeration dropdown field with the available couriers |
TikTok Delivery Options - Here we store the delivery options and their desription.
Field Name | Type | Required | Comment | Default Value |
---|---|---|---|---|
Delivery option ID | Textbox | No | Id of the delivery option | |
Descriptoin | Textbox | No | The description of the delivery option |
Signature algorithm
When calling a TikTok Shop API, a signature must be included to properly authenticate your request. All requests that do not include a signature, or have an invalid signature, will be denied access.
TikTok Shop APIs use HMAC-SHA256 as the default algorithm for generating signatures.
HS256 (HMAC with SHA-256) is a symmetric algorithm, meaning there is only one private key that is shared between the two parties. Since the same key is used to both generate and validate the signature, care must be taken to ensure that the key is not compromised.
When TikTok Shop receives an authenticated request, it recreates the signature using the authentication information contained in the request. If the signatures match, the service processes the request. Otherwise, it rejects the request.
Use the following code to generate your API request signature:
📌 Note: The following code is written in Go, so we need to translate this to php
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"io"
"net/http"
"sort"
)
// secret: App secret
func CalSign(req *http.Request, secret string) string {
queries := req.URL.Query()
// extract all query parameters excluding sign and access_token
keys := make([]string, len(queries))
idx := 0
for k := range queries {
// params except 'sign' & 'access_token'
if k != "sign" && k != "access_token" {
keys[idx] = k
idx++
}
}
// reorder the parameters' key in alphabetical order
sort.Slice(keys, func(i, j int) bool {
return keys[i] < keys[j]
})
// Concatenate all the parameters in the format of {key}{value}
input := ""
for _, key := range keys {
input = input + key + queries.Get(key)
}
// append the request path
input = req.URL.Path + input
// if the request header Content-type is not multipart/form-data, append body to the end
mediaType, _, _ := mime.ParseMediaType(req.Header.Get("Content-type"))
if mediaType != "multipart/form-data" {
body, _ := io.ReadAll(req.Body)
input = input + string(body)
req.Body.Close()
// reset body after reading from the original
req.Body = io.NopCloser(bytes.NewReader(body))
}
// wrap the string generated in step 5 with the App secret
input = secret + input + secret
return generateSHA256(input, secret)
}
func generateSHA256(input, secret string) string {
// encode the digest byte stream in hexadecimal and use sha256 to generate sign with salt(secret)
h := hmac.New(sha256.New, []byte(secret))
if _, err := h.Write([]byte(input)); err != nil {
// TODO error log
return ""
}
return hex.EncodeToString(h.Sum(nil))
}
More information & detailed step-by-step sign generation algorithm can be found in the documentation link provided above.
Common mistakes
What is the most common reason for encountering the "signature is invalid" error during an API call?
- Frequently, this error occurs when developers use incorrect app keys and secrets to generate the signature. It's essential to verify that the app key and secret match precisely.
- Ensure
sign
andaccess_token
are not included in the reordered query keys (Step 2). - Always ensure that you are using the HMAC-SHA256 signature method (different from regular SHA-256).
- Ensure that the timestamp is within 5 minutes of the current time when the platform receives the request. The timestamp must be represented as a 10-digit Unix timestamp.
</v1.1>