mirror of
https://github.com/larksuite/cli.git
synced 2026-07-07 00:55:53 +08:00
132 lines
3.3 KiB
Go
132 lines
3.3 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package auth
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/larksuite/cli/errs"
|
|
"github.com/larksuite/cli/internal/core"
|
|
)
|
|
|
|
// RevokeToken revokes a previously issued OAuth token.
|
|
func RevokeToken(httpClient *http.Client, appId, appSecret string, brand core.LarkBrand, token, tokenTypeHint string) error {
|
|
endpoints := ResolveOAuthEndpoints(brand)
|
|
|
|
form := url.Values{}
|
|
form.Set("client_id", appId)
|
|
form.Set("client_secret", appSecret)
|
|
form.Set("token", token)
|
|
if tokenTypeHint != "" {
|
|
form.Set("token_type_hint", tokenTypeHint)
|
|
}
|
|
|
|
req, err := http.NewRequest(http.MethodPost, endpoints.Revoke, strings.NewReader(form.Encode()))
|
|
if err != nil {
|
|
return errs.NewInternalError(errs.SubtypeUnknown, "token revoke request creation failed: %v", err).WithCause(err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
resp, err := httpClient.Do(req)
|
|
if err != nil {
|
|
return errs.NewNetworkError(errs.SubtypeNetworkTransport, "token revoke transport error: %v", err).WithCause(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
logHTTPResponse(resp)
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return errs.NewInternalError(errs.SubtypeInvalidResponse, "token revoke read error: %v", err).WithCause(err)
|
|
}
|
|
|
|
if resp.StatusCode >= 400 {
|
|
return revokeHTTPStatusError(resp.StatusCode, body)
|
|
}
|
|
|
|
if len(body) == 0 {
|
|
return nil
|
|
}
|
|
|
|
var data map[string]interface{}
|
|
if err := json.Unmarshal(body, &data); err != nil {
|
|
return nil
|
|
}
|
|
|
|
if code := getInt(data, "code", 0); code != 0 {
|
|
msg := getStr(data, "msg")
|
|
if msg == "" {
|
|
msg = getStr(data, "message")
|
|
}
|
|
if msg == "" {
|
|
msg = "unknown error"
|
|
}
|
|
return errs.NewAPIError(errs.SubtypeUnknown, "token revoke failed [%d]: %s", code, msg).
|
|
WithCode(code).
|
|
WithCause(errors.New(msg))
|
|
}
|
|
|
|
if errStr := getStr(data, "error"); errStr != "" {
|
|
msg := getStr(data, "error_description")
|
|
if msg == "" {
|
|
msg = errStr
|
|
}
|
|
return errs.NewAPIError(errs.SubtypeUnknown, "token revoke failed: %s", msg).
|
|
WithCause(errors.New(msg))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func revokeHTTPStatusError(status int, body []byte) error {
|
|
msg := formatOAuthErrorBody(body)
|
|
cause := errors.New(strings.TrimSpace(string(body)))
|
|
if strings.TrimSpace(string(body)) == "" {
|
|
cause = errors.New(msg)
|
|
}
|
|
if status >= http.StatusInternalServerError {
|
|
return errs.NewNetworkError(errs.SubtypeNetworkServer, "token revoke failed: HTTP %d: %s", status, msg).
|
|
WithCode(status).
|
|
WithRetryable().
|
|
WithCause(cause)
|
|
}
|
|
subtype := errs.SubtypeUnknown
|
|
if status == http.StatusNotFound {
|
|
subtype = errs.SubtypeNotFound
|
|
}
|
|
return errs.NewAPIError(subtype, "token revoke failed: HTTP %d: %s", status, msg).
|
|
WithCode(status).
|
|
WithCause(cause)
|
|
}
|
|
|
|
func formatOAuthErrorBody(body []byte) string {
|
|
trimmed := strings.TrimSpace(string(body))
|
|
if trimmed == "" {
|
|
return "empty response"
|
|
}
|
|
|
|
var data map[string]interface{}
|
|
if err := json.Unmarshal(body, &data); err != nil {
|
|
return trimmed
|
|
}
|
|
|
|
if msg := getStr(data, "error_description"); msg != "" {
|
|
return msg
|
|
}
|
|
if msg := getStr(data, "msg"); msg != "" {
|
|
return msg
|
|
}
|
|
if msg := getStr(data, "message"); msg != "" {
|
|
return msg
|
|
}
|
|
if msg := getStr(data, "error"); msg != "" {
|
|
return msg
|
|
}
|
|
return trimmed
|
|
}
|