You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
3.4 KiB
125 lines
3.4 KiB
package facebook_auth
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/coze-dev/coze-go"
|
|
)
|
|
|
|
func newSys(options Options) (sys *Facebook, err error) {
|
|
sys = &Facebook{
|
|
options: options,
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
type Facebook struct {
|
|
options Options
|
|
api coze.CozeAPI
|
|
}
|
|
|
|
// func (this *Facebook) Auth(ctx context.Context, accessToken string) (info *FacebookTokenInfo, err error) {
|
|
// // 调用 Facebook 的 Debug Token 接口
|
|
// url := fmt.Sprintf(
|
|
// "https://graph.facebook.com/debug_token?input_token=%s&access_token=%s|%s",
|
|
// accessToken, this.options.AppID, this.options.AppSecret,
|
|
// )
|
|
|
|
// resp, err := http.Get(url)
|
|
// if err != nil {
|
|
// return nil, fmt.Errorf("failed to call Facebook API: %v", err)
|
|
// }
|
|
// defer resp.Body.Close()
|
|
|
|
// body, err := ioutil.ReadAll(resp.Body)
|
|
// if err != nil {
|
|
// return nil, fmt.Errorf("failed to read response body: %v", err)
|
|
// }
|
|
|
|
// // 解析 Facebook 的响应
|
|
// var tokenResponse FacebookTokenDebugResponse
|
|
// if err := json.Unmarshal(body, &tokenResponse); err != nil {
|
|
// return nil, fmt.Errorf("failed to parse Facebook response: %v", err)
|
|
// }
|
|
|
|
// // 检查 Token 是否有效
|
|
// if !tokenResponse.Data.IsValid {
|
|
// return nil, fmt.Errorf("token is invalid")
|
|
// }
|
|
|
|
// // 检查 App ID 是否匹配
|
|
// if tokenResponse.Data.AppID != this.options.AppID {
|
|
// return nil, fmt.Errorf("invalid app ID")
|
|
// }
|
|
|
|
// // 返回用户 ID
|
|
// return tokenResponse.Data, nil
|
|
// }
|
|
|
|
func (f *Facebook) Auth(ctx context.Context, accessToken string) (*FacebookTokenInfo, error) {
|
|
// 1. 创建带超时和重试的 HTTP 客户端
|
|
client := &http.Client{
|
|
Timeout: 10 * time.Second, // 避免无限等待
|
|
}
|
|
|
|
// 2. 编码 URL 参数,防止特殊字符问题
|
|
encodedToken := url.QueryEscape(accessToken)
|
|
url := fmt.Sprintf(
|
|
"https://graph.facebook.com/debug_token?input_token=%s&access_token=%s|%s",
|
|
encodedToken,
|
|
url.QueryEscape(f.options.AppID),
|
|
url.QueryEscape(f.options.AppSecret),
|
|
)
|
|
|
|
// 3. 添加上下文支持(如超时或取消)
|
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
|
|
// 4. 发送请求并处理响应
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Facebook API request failed: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// 5. 检查 HTTP 状态码
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := ioutil.ReadAll(resp.Body)
|
|
return nil, fmt.Errorf("Facebook API returned %d: %s", resp.StatusCode, string(body))
|
|
}
|
|
|
|
// 6. 解析响应体
|
|
var tokenResponse FacebookTokenDebugResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&tokenResponse); err != nil {
|
|
return nil, fmt.Errorf("failed to decode Facebook response: %w", err)
|
|
}
|
|
|
|
// 7. 检查 Token 有效性及错误信息
|
|
if tokenResponse.Data.Error != nil {
|
|
return nil, fmt.Errorf("Facebook token error [%d]: %s",
|
|
tokenResponse.Data.Error.Code,
|
|
tokenResponse.Data.Error.Message)
|
|
}
|
|
|
|
if !tokenResponse.Data.IsValid {
|
|
return nil, errors.New("token is invalid (no error details provided)")
|
|
}
|
|
|
|
// 8. 严格检查 AppID 类型(Facebook 返回的 AppID 可能是字符串或数字)
|
|
if fmt.Sprintf("%v", tokenResponse.Data.AppID) != f.options.AppID {
|
|
return nil, fmt.Errorf("app ID mismatch: expected %s, got %v",
|
|
f.options.AppID, tokenResponse.Data.AppID)
|
|
}
|
|
|
|
return tokenResponse.Data, nil
|
|
}
|
|
|