From f1ef89ef55bec5159414492291b7e092962a8232 Mon Sep 17 00:00:00 2001 From: tigerenwork Date: Mon, 29 Jun 2026 00:28:39 +0800 Subject: [PATCH] fix: tenant_access_token response is flat, not wrapped in data key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The /auth/v3/tenant_access_token/internal endpoint, like the OAuth token endpoints, returns code, msg, tenant_access_token, and expire at the top level of the JSON response — NOT nested under a 'data' key. FeishuAPIResponse decoded successfully but with data=nil (extra fields silently ignored by JSONDecoder). The guard then threw FeishuError.apiError(code: 0, message: 'ok') — appearing to succeed but always failing. This blocked the subsequent OAuth token exchange call. - Added code/msg fields to TenantAccessTokenData - tenantAccessToken() now decodes TenantAccessTokenData directly and checks data.code != 0 instead of going through FeishuAPIResponse Co-Authored-By: Claude --- Sources/Services/Feishu/FeishuAuthService.swift | 13 ++++++------- Sources/Services/Feishu/FeishuModels.swift | 4 ++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Sources/Services/Feishu/FeishuAuthService.swift b/Sources/Services/Feishu/FeishuAuthService.swift index 13b3361..f8e6e47 100644 --- a/Sources/Services/Feishu/FeishuAuthService.swift +++ b/Sources/Services/Feishu/FeishuAuthService.swift @@ -102,14 +102,13 @@ final class FeishuAuthService { var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") - request.httpBody = try JSONEncoder().encode([ - "app_id": appId, - "app_secret": appSecret - ]) + let body: [String: String] = ["app_id": appId, "app_secret": appSecret] + request.httpBody = try JSONEncoder().encode(body) - let response: FeishuAPIResponse = try await send(request) - guard let data = response.data else { - throw FeishuError.apiError(code: response.code, message: response.msg) + // This endpoint returns code/msg/tenant_access_token/expire all at top level + let data: TenantAccessTokenData = try await send(request) + guard data.code == 0 else { + throw FeishuError.apiError(code: data.code, message: data.msg) } cachedTenantToken = data.tenantAccessToken diff --git a/Sources/Services/Feishu/FeishuModels.swift b/Sources/Services/Feishu/FeishuModels.swift index 6d98b5a..f99e18e 100644 --- a/Sources/Services/Feishu/FeishuModels.swift +++ b/Sources/Services/Feishu/FeishuModels.swift @@ -40,10 +40,14 @@ struct OAuthTokenData: Decodable { } struct TenantAccessTokenData: Decodable { + let code: Int + let msg: String let tenantAccessToken: String let expire: Int enum CodingKeys: String, CodingKey { + case code + case msg case tenantAccessToken = "tenant_access_token" case expire }