From cf9de54792434a5e2b3bc3a7ad526f7125ac4f42 Mon Sep 17 00:00:00 2001 From: tigerenwork Date: Mon, 29 Jun 2026 00:13:48 +0800 Subject: [PATCH] fix: decode OAuth token response directly, not via FeishuAPIResponse wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The /authen/v1/oidc/access_token and refresh_access_token endpoints return token fields (access_token, refresh_token, expires_in) at the top level of the JSON response, not nested under a 'data' key like standard Feishu API responses. This caused 'Feishu API error (0): ok' — code 0 with no wrapped data. Changed exchangeCode and refreshAccessToken to decode OAuthTokenData directly from the response. Removed unused decodeAPIResponse helper. Also removed redirect_uri from token exchange body (not required by Feishu's token endpoint). Co-Authored-By: Claude --- .../Services/Feishu/FeishuAuthService.swift | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/Sources/Services/Feishu/FeishuAuthService.swift b/Sources/Services/Feishu/FeishuAuthService.swift index 3e1cb77..80aeb4d 100644 --- a/Sources/Services/Feishu/FeishuAuthService.swift +++ b/Sources/Services/Feishu/FeishuAuthService.swift @@ -51,9 +51,9 @@ final class FeishuAuthService { request.httpBody = try JSONEncoder().encode([ "grant_type": "authorization_code", "code": code, - "redirect_uri": redirectURI, ] as [String: String]) - return try await decodeAPIResponse(request) + // Token endpoints return data at top level, not wrapped in {code, msg, data} + return try await send(request) } func refreshAccessToken(_ refreshToken: String) async throws -> OAuthTokenData { @@ -65,9 +65,10 @@ final class FeishuAuthService { request.setValue("Bearer \(tenantToken)", forHTTPHeaderField: "Authorization") request.httpBody = try JSONEncoder().encode([ "grant_type": "refresh_token", - "refresh_token": refreshToken - ]) - return try await decodeAPIResponse(request) + "refresh_token": refreshToken, + ] as [String: String]) + // Token endpoints return data at top level, not wrapped in {code, msg, data} + return try await send(request) } func fetchCurrentUserName(accessToken: String) async throws -> String { @@ -112,14 +113,6 @@ final class FeishuAuthService { return data.tenantAccessToken } - private func decodeAPIResponse(_ request: URLRequest) async throws -> T { - let response: FeishuAPIResponse = try await send(request) - guard response.code == 0, let data = response.data else { - throw FeishuError.apiError(code: response.code, message: response.msg) - } - return data - } - private func send(_ request: URLRequest) async throws -> T { let (data, response) = try await session.data(for: request) guard let httpResponse = response as? HTTPURLResponse else {