Files
CLIProxyAPIPlus/test_auth_idc_go1.go

324 lines
9.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 测试脚本 1模拟 kiro2api_go1 的 IdC 认证方式
// 这个脚本完整模拟 kiro-gateway/temp/kiro2api_go1 的认证逻辑
// 运行方式: go run test_auth_idc_go1.go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"math/rand"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
// 配置常量 - 来自 kiro2api_go1/config/config.go
const (
IdcRefreshTokenURL = "https://oidc.us-east-1.amazonaws.com/token"
CodeWhispererAPIURL = "https://codewhisperer.us-east-1.amazonaws.com"
)
// AuthConfig - 来自 kiro2api_go1/auth/config.go
type AuthConfig struct {
AuthType string `json:"auth"`
RefreshToken string `json:"refreshToken"`
ClientID string `json:"clientId,omitempty"`
ClientSecret string `json:"clientSecret,omitempty"`
}
// IdcRefreshRequest - 来自 kiro2api_go1/types/token.go
type IdcRefreshRequest struct {
ClientId string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
GrantType string `json:"grantType"`
RefreshToken string `json:"refreshToken"`
}
// RefreshResponse - 来自 kiro2api_go1/types/token.go
type RefreshResponse struct {
AccessToken string `json:"accessToken"`
RefreshToken string `json:"refreshToken,omitempty"`
ExpiresIn int `json:"expiresIn"`
TokenType string `json:"tokenType,omitempty"`
}
// Fingerprint - 简化的指纹结构
type Fingerprint struct {
OSType string
ConnectionBehavior string
AcceptLanguage string
SecFetchMode string
AcceptEncoding string
}
func generateFingerprint() *Fingerprint {
osTypes := []string{"darwin", "windows", "linux"}
connections := []string{"keep-alive", "close"}
languages := []string{"en-US,en;q=0.9", "zh-CN,zh;q=0.9", "en-GB,en;q=0.9"}
fetchModes := []string{"cors", "navigate", "no-cors"}
return &Fingerprint{
OSType: osTypes[rand.Intn(len(osTypes))],
ConnectionBehavior: connections[rand.Intn(len(connections))],
AcceptLanguage: languages[rand.Intn(len(languages))],
SecFetchMode: fetchModes[rand.Intn(len(fetchModes))],
AcceptEncoding: "gzip, deflate, br",
}
}
func main() {
rand.Seed(time.Now().UnixNano())
fmt.Println("=" + strings.Repeat("=", 59))
fmt.Println(" 测试脚本 1: kiro2api_go1 风格 IdC 认证")
fmt.Println("=" + strings.Repeat("=", 59))
// Step 1: 加载官方格式的 token 文件
fmt.Println("\n[Step 1] 加载官方格式 Token 文件")
fmt.Println("-" + strings.Repeat("-", 59))
// 尝试从多个位置加载
tokenPaths := []string{
// 优先使用包含完整 clientId/clientSecret 的文件
"E:/ai_project_2api/kiro-gateway/configs/kiro/kiro-auth-token-1768317098.json",
filepath.Join(os.Getenv("USERPROFILE"), ".aws", "sso", "cache", "kiro-auth-token.json"),
}
var tokenData map[string]interface{}
var loadedPath string
for _, p := range tokenPaths {
data, err := os.ReadFile(p)
if err == nil {
if err := json.Unmarshal(data, &tokenData); err == nil {
loadedPath = p
break
}
}
}
if tokenData == nil {
fmt.Println("❌ 无法加载任何 token 文件")
return
}
fmt.Printf("✅ 加载文件: %s\n", loadedPath)
// 提取关键字段
accessToken, _ := tokenData["accessToken"].(string)
refreshToken, _ := tokenData["refreshToken"].(string)
clientId, _ := tokenData["clientId"].(string)
clientSecret, _ := tokenData["clientSecret"].(string)
authMethod, _ := tokenData["authMethod"].(string)
region, _ := tokenData["region"].(string)
if region == "" {
region = "us-east-1"
}
fmt.Printf("\n当前 Token 信息:\n")
fmt.Printf(" AuthMethod: %s\n", authMethod)
fmt.Printf(" Region: %s\n", region)
fmt.Printf(" AccessToken: %s...\n", truncate(accessToken, 50))
fmt.Printf(" RefreshToken: %s...\n", truncate(refreshToken, 50))
fmt.Printf(" ClientID: %s\n", truncate(clientId, 30))
fmt.Printf(" ClientSecret: %s...\n", truncate(clientSecret, 50))
// Step 2: 验证 IdC 认证所需字段
fmt.Println("\n[Step 2] 验证 IdC 认证必需字段")
fmt.Println("-" + strings.Repeat("-", 59))
missingFields := []string{}
if refreshToken == "" {
missingFields = append(missingFields, "refreshToken")
}
if clientId == "" {
missingFields = append(missingFields, "clientId")
}
if clientSecret == "" {
missingFields = append(missingFields, "clientSecret")
}
if len(missingFields) > 0 {
fmt.Printf("❌ 缺少必需字段: %v\n", missingFields)
fmt.Println(" IdC 认证需要: refreshToken, clientId, clientSecret")
return
}
fmt.Println("✅ 所有必需字段都存在")
// Step 3: 测试直接使用 accessToken 调用 API
fmt.Println("\n[Step 3] 测试当前 AccessToken 有效性")
fmt.Println("-" + strings.Repeat("-", 59))
if testAPICall(accessToken, region) {
fmt.Println("✅ 当前 AccessToken 有效,无需刷新")
} else {
fmt.Println("⚠️ 当前 AccessToken 无效,需要刷新")
// Step 4: 使用 kiro2api_go1 风格刷新 token
fmt.Println("\n[Step 4] 使用 kiro2api_go1 风格刷新 Token")
fmt.Println("-" + strings.Repeat("-", 59))
newToken, err := refreshIdCToken(AuthConfig{
AuthType: "IdC",
RefreshToken: refreshToken,
ClientID: clientId,
ClientSecret: clientSecret,
}, region)
if err != nil {
fmt.Printf("❌ 刷新失败: %v\n", err)
return
}
fmt.Println("✅ Token 刷新成功!")
fmt.Printf(" 新 AccessToken: %s...\n", truncate(newToken.AccessToken, 50))
fmt.Printf(" ExpiresIn: %d 秒\n", newToken.ExpiresIn)
// Step 5: 验证新 token
fmt.Println("\n[Step 5] 验证新 Token")
fmt.Println("-" + strings.Repeat("-", 59))
if testAPICall(newToken.AccessToken, region) {
fmt.Println("✅ 新 Token 验证成功!")
// 保存新 token
saveNewToken(loadedPath, newToken, tokenData)
} else {
fmt.Println("❌ 新 Token 验证失败")
}
}
fmt.Println("\n" + strings.Repeat("=", 60))
fmt.Println(" 测试完成")
fmt.Println(strings.Repeat("=", 60))
}
// refreshIdCToken - 完全模拟 kiro2api_go1/auth/refresh.go 的 refreshIdCToken 函数
func refreshIdCToken(authConfig AuthConfig, region string) (*RefreshResponse, error) {
refreshReq := IdcRefreshRequest{
ClientId: authConfig.ClientID,
ClientSecret: authConfig.ClientSecret,
GrantType: "refresh_token",
RefreshToken: authConfig.RefreshToken,
}
reqBody, err := json.Marshal(refreshReq)
if err != nil {
return nil, fmt.Errorf("序列化IdC请求失败: %v", err)
}
url := fmt.Sprintf("https://oidc.%s.amazonaws.com/token", region)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqBody))
if err != nil {
return nil, fmt.Errorf("创建IdC请求失败: %v", err)
}
// 设置 IdC 特殊 headers使用指纹随机化- 完全模拟 kiro2api_go1
fp := generateFingerprint()
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Host", fmt.Sprintf("oidc.%s.amazonaws.com", region))
req.Header.Set("Connection", fp.ConnectionBehavior)
req.Header.Set("x-amz-user-agent", fmt.Sprintf("aws-sdk-js/3.738.0 ua/2.1 os/%s lang/js md/browser#unknown_unknown api/sso-oidc#3.738.0 m/E KiroIDE", fp.OSType))
req.Header.Set("Accept", "*/*")
req.Header.Set("Accept-Language", fp.AcceptLanguage)
req.Header.Set("sec-fetch-mode", fp.SecFetchMode)
req.Header.Set("User-Agent", "node")
req.Header.Set("Accept-Encoding", fp.AcceptEncoding)
fmt.Println("发送刷新请求:")
fmt.Printf(" URL: %s\n", url)
fmt.Println(" Headers:")
for k, v := range req.Header {
if k == "Content-Type" || k == "Host" || k == "X-Amz-User-Agent" || k == "User-Agent" {
fmt.Printf(" %s: %s\n", k, v[0])
}
}
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("IdC请求失败: %v", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("IdC刷新失败: 状态码 %d, 响应: %s", resp.StatusCode, string(body))
}
var refreshResp RefreshResponse
if err := json.Unmarshal(body, &refreshResp); err != nil {
return nil, fmt.Errorf("解析IdC响应失败: %v", err)
}
return &refreshResp, nil
}
func testAPICall(accessToken, region string) bool {
url := fmt.Sprintf("https://codewhisperer.%s.amazonaws.com", region)
payload := map[string]interface{}{
"origin": "AI_EDITOR",
"isEmailRequired": true,
"resourceType": "AGENTIC_REQUEST",
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/x-amz-json-1.0")
req.Header.Set("x-amz-target", "AmazonCodeWhispererService.GetUsageLimits")
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Accept", "application/json")
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
fmt.Printf(" 请求错误: %v\n", err)
return false
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
fmt.Printf(" API 响应: HTTP %d\n", resp.StatusCode)
if resp.StatusCode == 200 {
return true
}
fmt.Printf(" 错误详情: %s\n", truncate(string(respBody), 200))
return false
}
func saveNewToken(originalPath string, newToken *RefreshResponse, originalData map[string]interface{}) {
// 更新 token 数据
originalData["accessToken"] = newToken.AccessToken
if newToken.RefreshToken != "" {
originalData["refreshToken"] = newToken.RefreshToken
}
originalData["expiresAt"] = time.Now().Add(time.Duration(newToken.ExpiresIn) * time.Second).Format(time.RFC3339)
data, _ := json.MarshalIndent(originalData, "", " ")
// 保存到新文件
newPath := strings.TrimSuffix(originalPath, ".json") + "_refreshed.json"
if err := os.WriteFile(newPath, data, 0644); err != nil {
fmt.Printf("⚠️ 保存失败: %v\n", err)
} else {
fmt.Printf("✅ 新 Token 已保存到: %s\n", newPath)
}
}
func truncate(s string, n int) string {
if len(s) <= n {
return s
}
return s[:n]
}