fix: OAuth token endpoint wraps in {code,message,data} — not flat
The /authen/v1/oidc/access_token response uses the standard Feishu envelope with 'message' (not 'msg') field. Updated FeishuAPIResponse to handle both 'msg' and 'message' via custom init(from:). exchangeCode and refreshAccessToken now decode via FeishuAPIResponse<OAuthTokenData>. Tenant token endpoint (/auth/v3/tenant_access_token/internal) returns flat response — TenantAccessTokenData decoded directly. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
ae918a379c
commit
0f8565f65d
|
|
@ -50,11 +50,14 @@ final class FeishuAuthService {
|
||||||
request.httpMethod = "POST"
|
request.httpMethod = "POST"
|
||||||
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||||
request.setValue("Bearer \(tenantToken)", forHTTPHeaderField: "Authorization")
|
request.setValue("Bearer \(tenantToken)", forHTTPHeaderField: "Authorization")
|
||||||
// Use manual JSON to rule out JSONEncoder issues
|
|
||||||
let jsonBody = "{\"grant_type\":\"authorization_code\",\"code\":\"\(code)\"}"
|
let jsonBody = "{\"grant_type\":\"authorization_code\",\"code\":\"\(code)\"}"
|
||||||
request.httpBody = jsonBody.data(using: .utf8)
|
request.httpBody = jsonBody.data(using: .utf8)
|
||||||
print("[BUGGER] exchangeCode: sending to \(url.absoluteString)...")
|
print("[BUGGER] exchangeCode: sending to \(url.absoluteString)...")
|
||||||
let tokenData: OAuthTokenData = try await send(request)
|
// OAuth token response is wrapped in {code, message, data: {...}}
|
||||||
|
let response: FeishuAPIResponse<OAuthTokenData> = try await send(request)
|
||||||
|
guard response.code == 0, let tokenData = response.data else {
|
||||||
|
throw FeishuError.apiError(code: response.code, message: response.msg)
|
||||||
|
}
|
||||||
print("[BUGGER] exchangeCode: SUCCESS access=\(tokenData.accessToken.prefix(8))... refresh=\(tokenData.refreshToken?.prefix(8) ?? "nil")...")
|
print("[BUGGER] exchangeCode: SUCCESS access=\(tokenData.accessToken.prefix(8))... refresh=\(tokenData.refreshToken?.prefix(8) ?? "nil")...")
|
||||||
return tokenData
|
return tokenData
|
||||||
}
|
}
|
||||||
|
|
@ -66,12 +69,13 @@ final class FeishuAuthService {
|
||||||
request.httpMethod = "POST"
|
request.httpMethod = "POST"
|
||||||
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||||
request.setValue("Bearer \(tenantToken)", forHTTPHeaderField: "Authorization")
|
request.setValue("Bearer \(tenantToken)", forHTTPHeaderField: "Authorization")
|
||||||
request.httpBody = try JSONEncoder().encode([
|
let jsonBody = "{\"grant_type\":\"refresh_token\",\"refresh_token\":\"\(refreshToken)\"}"
|
||||||
"grant_type": "refresh_token",
|
request.httpBody = jsonBody.data(using: .utf8)
|
||||||
"refresh_token": refreshToken,
|
// Uses same {code, message, data: {...}} wrapper as exchangeCode
|
||||||
] as [String: String])
|
let response: FeishuAPIResponse<OAuthTokenData> = try await send(request)
|
||||||
// Token endpoints return data at top level, not wrapped in {code, msg, data}
|
guard response.code == 0, let tokenData = response.data else {
|
||||||
let tokenData: OAuthTokenData = try await send(request)
|
throw FeishuError.apiError(code: response.code, message: response.msg)
|
||||||
|
}
|
||||||
return tokenData
|
return tokenData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,22 @@ struct FeishuAPIResponse<T: Decodable>: Decodable {
|
||||||
let code: Int
|
let code: Int
|
||||||
let msg: String
|
let msg: String
|
||||||
let data: T?
|
let data: T?
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case code, msg, message, data
|
||||||
|
}
|
||||||
|
|
||||||
|
init(from decoder: Decoder) throws {
|
||||||
|
let c = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
code = try c.decode(Int.self, forKey: .code)
|
||||||
|
// Some endpoints use "msg", others use "message"
|
||||||
|
if let m = try c.decodeIfPresent(String.self, forKey: .msg) {
|
||||||
|
msg = m
|
||||||
|
} else {
|
||||||
|
msg = try c.decode(String.self, forKey: .message)
|
||||||
|
}
|
||||||
|
data = try c.decodeIfPresent(T.self, forKey: .data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RecordListData: Decodable {
|
struct RecordListData: Decodable {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue