yikart/AiToEarn · warning
TikTok webhook signature invalid
Error message
TikTok webhook signature invalid
What it means
The TikTok webhook provider rejects POST deliveries whose verify(request) check fails — TikTok signs payloads and the provider validates the signature against the configured secret. On failure it logs a warning and returns HTTP 401 with { status: 'invalid_signature' } instead of processing the event. (No AppException code is attached; the response body is the contract.)
Source
Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/tiktok/tiktok-webhook.provider.ts:55
@Injectable()
export class TikTokWebhookProvider implements PlatformWebhookHandler {
private readonly logger = new Logger(TikTokWebhookProvider.name)
constructor(
private readonly config: TiktokConfig,
@Optional() private readonly publishRecordRepo?: PublishRecordRepository,
@Optional() private readonly stateService?: PublishStateService,
) {}
async handle(request: Request, response: Response): Promise<void> {
if (request.method === 'GET') {
const query = request.query as TikTokWebhookChallengeQuery
response.status(query.challenge ? 200 : 404).send(query.challenge ?? '')
return
}
if (!this.verify(request)) {
this.logger.warn({ platform: AccountType.TikTok }, 'TikTok webhook signature invalid')
response.status(401).json({ status: 'invalid_signature' })
return
}
const body = this.parseTikTokBody(request)
if (!body) {
response.status(200).json({ status: 'ok' })
return
}
await this.applyTikTokPublishResult(body)
response.status(200).json({ status: 'ok' })
}
private verify(request: RawBodyRequest): boolean {
const rawBody = request.rawBody
const signatureHeader = this.getHeader(request, 'tiktok-signature')
?? this.getHeader(request, 'x-tiktok-signature')
?? this.getHeader(request, 'x-tt-signature')
if (!signatureHeader || !rawBody || !this.config.clientSecret) {View on GitHub (pinned to d3aa8bea5b)
Solutions
- Confirm the TikTok client secret in config matches the app registered for webhooks
- Verify signature over the raw request body bytes (rawBody), not re-serialized JSON
- Check the exact signature header name TikTok sends for your webhook event type and that verify() reads it
- Exclude the webhook route from body-rewriting middleware/proxies
Example fix
// before const body = JSON.stringify(request.body) const ok = verifySignature(body, request.headers['signature'], secret) // after const raw = request.rawBody // captured pre-parse const ok = raw ? verifySignature(raw, request.headers['signature'], secret) : false
Defensive patterns
Strategy: validation
Validate before calling
const expected = crypto.createHmac('sha256', process.env.TIKTOK_CLIENT_SECRET!).update(req.rawBody).digest('hex')
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(String(req.headers['signature'] ?? '')))) {
return res.status(401).json({ status: 'invalid_signature' })
} Type guard
function hasValidTikTokSignature(req: Request): boolean {
const sig = req.headers['signature']
return typeof sig === 'string' && Buffer.isBuffer((req as any).rawBody) && verifyHmac((req as any).rawBody, sig, secret)
} Try / catch
if (!hasValidTikTokSignature(req)) {
return res.status(401).json({ status: 'invalid_signature' })
} Prevention
- Verify over rawBody captured before body parsers
- Keep the TikTok app secret synced with the developer portal
- Monitor invalid_signature responses for spikes indicating secret or proxy changes
When it happens
Trigger: A POST to the TikTok webhook endpoint where the computed HMAC of the raw body does not match the signature header: wrong secret configured, body re-encoded by middleware, missing header, or a non-TikTok caller.
Common situations: TikTok app secret rotated without updating backend env; Express body-parser consumed the stream before signature verification; testing the endpoint manually; payload passed through a transforming proxy.
Related errors
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/730e6863662bc519.
Report an issue: GitHub.