{"record":{"id":"290c46182a4c4917","repo":"yikart/AiToEarn","slug":"channelauthorizationfailed","errorCode":"ChannelAuthorizationFailed","errorMessage":"ResponseCode.ChannelAuthorizationFailed","messagePattern":"ResponseCode\\.ChannelAuthorizationFailed","errorType":"exception","errorClass":"AppException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/douyin/douyin-auth.provider.ts","lineNumber":157,"sourceCode":"        country: userInfo.country,\n        eAccountRole: userInfo.eAccountRole,\n      },\n    }\n  }\n\n  private async exchangeMiniAppCallback(\n    input: AuthCallbackInput,\n    callback: DouyinMiniAppCallback,\n  ): Promise<CredentialResult> {\n    const sessionInfo = await this.douyinMiniAppService.code2Session(callback.token)\n    if (!sessionInfo?.openid) {\n      throw new AppException(ResponseCode.ChannelAccessTokenFailed)\n    }\n    const openId = sessionInfo.openid\n    const unionId = sessionInfo.unionid\n\n    if (this.config.authType !== AuthType.QrCode) {\n      throw new AppException(ResponseCode.ChannelAuthorizationFailed)\n    }\n    if (!callback.tickets) {\n      throw new AppException(ResponseCode.ChannelAuthorizationFailed)\n    }\n\n    const userDataTicket = callback.tickets[DOUYIN_MINIAPP_USER_DATA_SCOPE]\n    const videoBindTicket = callback.tickets[DOUYIN_MINIAPP_VIDEO_BIND_SCOPE]\n    const homepageCredential = await this.douyinMiniAppService.getUserAccessToken(userDataTicket)\n    const videoCredential = userDataTicket === videoBindTicket\n      ? homepageCredential\n      : await this.douyinMiniAppService.getUserAccessToken(videoBindTicket)\n\n    if (homepageCredential.openId !== openId || videoCredential.openId !== openId) {\n      throw new AppException(ResponseCode.ChannelAuthorizationFailed)\n    }\n\n    if (!this.hasMiniAppScope(homepageCredential.scope, DOUYIN_MINIAPP_USER_DATA_SCOPE)) {\n      throw new AppException(ResponseCode.ChannelAuthorizationFailed)","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/douyin/douyin-auth.provider.ts#L139-L175","documentation":"exchangeMiniAppCallback is the mini-app branch of the Douyin auth exchange, but it double-checks that the provider is actually configured for AuthType.QrCode. If config.authType is anything else, the mini-app callback is not a valid flow for this channel configuration and ChannelAuthorizationFailed is thrown — a configuration/flow mismatch, not a user denial.","triggerScenarios":"A mini-app callback reaches exchangeCode → exchangeMiniAppCallback while the Douyin channel config's authType is not QrCode (e.g. set to a web/OAuth type), so the guard `this.config.authType !== AuthType.QrCode` trips.","commonSituations":"Environment config pointing the channel at the wrong auth type, a mini-app client calling a backend configured for the web OAuth flow, or authType renamed/changed in a config migration.","solutions":["Set the Douyin channel config authType to AuthType.QrCode for environments serving the mini-app flow.","Verify the client is calling the callback endpoint that matches its flow (mini-app callback vs web OAuth redirect).","Review recent config/env changes to DOUYIN auth settings and redeploy with the correct authType."],"exampleFix":"// before (config)\n{ \"authType\": \"web_oauth\" }\n// after (config)\n{ \"authType\": \"qr_code\" } // AuthType.QrCode — required for mini-app callbacks","handlingStrategy":"validation","validationCode":"// before driving the mini-app flow, assert config supports it\nif (douyinConfig.authType !== AuthType.QrCode) {\n  throw new Error(`mini-app callback requires authType=QrCode, got ${douyinConfig.authType}`)\n}","typeGuard":"function supportsMiniAppFlow(cfg: { authType: AuthType }): cfg is { authType: AuthType.QrCode } {\n  return cfg.authType === AuthType.QrCode\n}","tryCatchPattern":"try {\n  await douyinAuth.exchangeCode(callback)\n} catch (e) {\n  if (e.code === 'ChannelAuthorizationFailed') {\n    logger.error('authType mismatch or consent failure — check channel config authType')\n  }\n  throw e\n}","preventionTips":["Keep authType config per environment in version-controlled settings.","Route mini-app callbacks only to providers configured for QrCode.","Add a startup assertion that mini-app-enabled channels use AuthType.QrCode."],"tags":["douyin","miniapp","oauth","configuration"],"backgroundTag":"oauth-auth-type-mismatch","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}