* feat: Add validation and account management functionality - Add validation for clientID and clientSecret in refreshOIDCToken function - Add weight field for load balancing priority in Account struct - Implement weighted轮询策略以根据账号权重分配选择概率。 - Add batch account management functionality including enabling, disabling, refreshing, and retrieving account details. - Update Kiro API version and adjust user agent strings to reflect new version numbers. - Update Kiro version and modify user agent strings and header settings. - Refactor model mapping to an ordered list for precise key matching. - Add account bulk actions and filtering toolbar to index.html * feat: Add logic to skip accounts with exhausted usage limits - Add logic to skip accounts with exhausted usage limits when selecting the next account.
104 lines
2.8 KiB
Go
104 lines
2.8 KiB
Go
package auth
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"kiro-api-proxy/config"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
// RefreshToken 刷新 access token
|
|
func RefreshToken(account *config.Account) (string, string, int64, error) {
|
|
if account.AuthMethod == "social" {
|
|
return refreshSocialToken(account.RefreshToken)
|
|
}
|
|
return refreshOIDCToken(account.RefreshToken, account.ClientID, account.ClientSecret, account.Region)
|
|
}
|
|
|
|
// refreshOIDCToken IdC/Builder ID token 刷新
|
|
func refreshOIDCToken(refreshToken, clientID, clientSecret, region string) (string, string, int64, error) {
|
|
if clientID == "" || clientSecret == "" {
|
|
return "", "", 0, fmt.Errorf("OIDC refresh requires clientId and clientSecret")
|
|
}
|
|
if region == "" {
|
|
region = "us-east-1"
|
|
}
|
|
|
|
url := fmt.Sprintf("https://oidc.%s.amazonaws.com/token", region)
|
|
|
|
payload := map[string]string{
|
|
"clientId": clientID,
|
|
"clientSecret": clientSecret,
|
|
"refreshToken": refreshToken,
|
|
"grantType": "refresh_token",
|
|
}
|
|
|
|
body, _ := json.Marshal(payload)
|
|
req, _ := http.NewRequest("POST", url, bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := httpClient.Do(req)
|
|
if err != nil {
|
|
return "", "", 0, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
respBody, _ := io.ReadAll(resp.Body)
|
|
return "", "", 0, fmt.Errorf("refresh failed: %d %s", resp.StatusCode, string(respBody))
|
|
}
|
|
|
|
var result struct {
|
|
AccessToken string `json:"accessToken"`
|
|
RefreshToken string `json:"refreshToken"`
|
|
ExpiresIn int `json:"expiresIn"`
|
|
}
|
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
return "", "", 0, err
|
|
}
|
|
|
|
expiresAt := time.Now().Unix() + int64(result.ExpiresIn)
|
|
return result.AccessToken, result.RefreshToken, expiresAt, nil
|
|
}
|
|
|
|
// refreshSocialToken Social (GitHub/Google) token 刷新
|
|
func refreshSocialToken(refreshToken string) (string, string, int64, error) {
|
|
url := "https://prod.us-east-1.auth.desktop.kiro.dev/refreshToken"
|
|
|
|
payload := map[string]string{
|
|
"refreshToken": refreshToken,
|
|
}
|
|
|
|
body, _ := json.Marshal(payload)
|
|
req, _ := http.NewRequest("POST", url, bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := httpClient.Do(req)
|
|
if err != nil {
|
|
return "", "", 0, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
respBody, _ := io.ReadAll(resp.Body)
|
|
return "", "", 0, fmt.Errorf("refresh failed: %d %s", resp.StatusCode, string(respBody))
|
|
}
|
|
|
|
var result struct {
|
|
AccessToken string `json:"accessToken"`
|
|
RefreshToken string `json:"refreshToken"`
|
|
ExpiresIn int `json:"expiresIn"`
|
|
}
|
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
return "", "", 0, err
|
|
}
|
|
|
|
expiresAt := time.Now().Unix() + int64(result.ExpiresIn)
|
|
return result.AccessToken, result.RefreshToken, expiresAt, nil
|
|
}
|