mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 04:50:52 +08:00
feat(auth): Use user info for iFlow auth identifier
This commit is contained in:
@@ -1013,13 +1013,17 @@ func (h *Handler) RequestIFlowToken(c *gin.Context) {
|
||||
}
|
||||
|
||||
tokenStorage := authSvc.CreateTokenStorage(tokenData)
|
||||
tokenStorage.Email = fmt.Sprintf("iflow-%d", time.Now().UnixMilli())
|
||||
identifier := strings.TrimSpace(tokenStorage.Email)
|
||||
if identifier == "" {
|
||||
identifier = fmt.Sprintf("iflow-%d", time.Now().UnixMilli())
|
||||
tokenStorage.Email = identifier
|
||||
}
|
||||
record := &coreauth.Auth{
|
||||
ID: fmt.Sprintf("iflow-%s.json", tokenStorage.Email),
|
||||
ID: fmt.Sprintf("iflow-%s.json", identifier),
|
||||
Provider: "iflow",
|
||||
FileName: fmt.Sprintf("iflow-%s.json", tokenStorage.Email),
|
||||
FileName: fmt.Sprintf("iflow-%s.json", identifier),
|
||||
Storage: tokenStorage,
|
||||
Metadata: map[string]any{"email": tokenStorage.Email, "api_key": tokenStorage.APIKey},
|
||||
Metadata: map[string]any{"email": identifier, "api_key": tokenStorage.APIKey},
|
||||
Attributes: map[string]string{"api_key": tokenStorage.APIKey},
|
||||
}
|
||||
|
||||
|
||||
@@ -141,61 +141,69 @@ func (ia *IFlowAuth) doTokenRequest(ctx context.Context, req *http.Request) (*IF
|
||||
return nil, fmt.Errorf("iflow token: missing access token in response")
|
||||
}
|
||||
|
||||
apiKey, errAPI := ia.FetchAPIKey(ctx, tokenResp.AccessToken)
|
||||
info, errAPI := ia.FetchUserInfo(ctx, tokenResp.AccessToken)
|
||||
if errAPI != nil {
|
||||
return nil, fmt.Errorf("iflow token: fetch api key failed: %w", errAPI)
|
||||
return nil, fmt.Errorf("iflow token: fetch user info failed: %w", errAPI)
|
||||
}
|
||||
if strings.TrimSpace(apiKey) == "" {
|
||||
if strings.TrimSpace(info.APIKey) == "" {
|
||||
return nil, fmt.Errorf("iflow token: empty api key returned")
|
||||
}
|
||||
data.APIKey = apiKey
|
||||
email := strings.TrimSpace(info.Email)
|
||||
if email == "" {
|
||||
email = strings.TrimSpace(info.Phone)
|
||||
}
|
||||
if email == "" {
|
||||
return nil, fmt.Errorf("iflow token: missing account email/phone in user info")
|
||||
}
|
||||
data.APIKey = info.APIKey
|
||||
data.Email = email
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// FetchAPIKey retrieves the account API key associated with the provided access token.
|
||||
func (ia *IFlowAuth) FetchAPIKey(ctx context.Context, accessToken string) (string, error) {
|
||||
// FetchUserInfo retrieves account metadata (including API key) for the provided access token.
|
||||
func (ia *IFlowAuth) FetchUserInfo(ctx context.Context, accessToken string) (*userInfoData, error) {
|
||||
if strings.TrimSpace(accessToken) == "" {
|
||||
return "", fmt.Errorf("iflow api key: access token is empty")
|
||||
return nil, fmt.Errorf("iflow api key: access token is empty")
|
||||
}
|
||||
|
||||
endpoint := fmt.Sprintf("%s?accessToken=%s", iFlowUserInfoEndpoint, url.QueryEscape(accessToken))
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("iflow api key: create request failed: %w", err)
|
||||
return nil, fmt.Errorf("iflow api key: create request failed: %w", err)
|
||||
}
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
resp, err := ia.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("iflow api key: request failed: %w", err)
|
||||
return nil, fmt.Errorf("iflow api key: request failed: %w", err)
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("iflow api key: read response failed: %w", err)
|
||||
return nil, fmt.Errorf("iflow api key: read response failed: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Debugf("iflow api key failed: status=%d body=%s", resp.StatusCode, string(body))
|
||||
return "", fmt.Errorf("iflow api key: %d %s", resp.StatusCode, strings.TrimSpace(string(body)))
|
||||
return nil, fmt.Errorf("iflow api key: %d %s", resp.StatusCode, strings.TrimSpace(string(body)))
|
||||
}
|
||||
|
||||
var result userInfoResponse
|
||||
if err = json.Unmarshal(body, &result); err != nil {
|
||||
return "", fmt.Errorf("iflow api key: decode body failed: %w", err)
|
||||
return nil, fmt.Errorf("iflow api key: decode body failed: %w", err)
|
||||
}
|
||||
|
||||
if !result.Success {
|
||||
return "", fmt.Errorf("iflow api key: request not successful")
|
||||
return nil, fmt.Errorf("iflow api key: request not successful")
|
||||
}
|
||||
|
||||
if result.Data.APIKey == "" {
|
||||
return "", fmt.Errorf("iflow api key: missing api key in response")
|
||||
return nil, fmt.Errorf("iflow api key: missing api key in response")
|
||||
}
|
||||
|
||||
return result.Data.APIKey, nil
|
||||
return &result.Data, nil
|
||||
}
|
||||
|
||||
// CreateTokenStorage converts token data into persistence storage.
|
||||
@@ -209,6 +217,7 @@ func (ia *IFlowAuth) CreateTokenStorage(data *IFlowTokenData) *IFlowTokenStorage
|
||||
LastRefresh: time.Now().Format(time.RFC3339),
|
||||
Expire: data.Expire,
|
||||
APIKey: data.APIKey,
|
||||
Email: data.Email,
|
||||
TokenType: data.TokenType,
|
||||
Scope: data.Scope,
|
||||
}
|
||||
@@ -226,6 +235,9 @@ func (ia *IFlowAuth) UpdateTokenStorage(storage *IFlowTokenStorage, data *IFlowT
|
||||
if data.APIKey != "" {
|
||||
storage.APIKey = data.APIKey
|
||||
}
|
||||
if data.Email != "" {
|
||||
storage.Email = data.Email
|
||||
}
|
||||
storage.TokenType = data.TokenType
|
||||
storage.Scope = data.Scope
|
||||
}
|
||||
@@ -247,13 +259,17 @@ type IFlowTokenData struct {
|
||||
Scope string
|
||||
Expire string
|
||||
APIKey string
|
||||
Email string
|
||||
}
|
||||
|
||||
// userInfoResponse represents the structure returned by the user info endpoint.
|
||||
type userInfoResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Data struct {
|
||||
APIKey string `json:"apiKey"`
|
||||
Email string `json:"email"`
|
||||
} `json:"data"`
|
||||
Success bool `json:"success"`
|
||||
Data userInfoData `json:"data"`
|
||||
}
|
||||
|
||||
type userInfoData struct {
|
||||
APIKey string `json:"apiKey"`
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
|
||||
@@ -102,31 +102,14 @@ func (a *IFlowAuthenticator) Login(ctx context.Context, cfg *config.Config, opts
|
||||
|
||||
tokenStorage := authSvc.CreateTokenStorage(tokenData)
|
||||
|
||||
email := ""
|
||||
if opts.Metadata != nil {
|
||||
email = opts.Metadata["email"]
|
||||
if email == "" {
|
||||
email = opts.Metadata["alias"]
|
||||
}
|
||||
}
|
||||
|
||||
if email == "" && opts.Prompt != nil {
|
||||
email, err = opts.Prompt("Please input your email address or alias for iFlow:")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
email = strings.TrimSpace(email)
|
||||
email := strings.TrimSpace(tokenStorage.Email)
|
||||
if email == "" {
|
||||
return nil, &EmailRequiredError{Prompt: "Please provide an email address or alias for iFlow."}
|
||||
return nil, fmt.Errorf("iflow authentication failed: missing account identifier")
|
||||
}
|
||||
|
||||
tokenStorage.Email = email
|
||||
|
||||
fileName := fmt.Sprintf("iflow-%s.json", tokenStorage.Email)
|
||||
fileName := fmt.Sprintf("iflow-%s.json", email)
|
||||
metadata := map[string]any{
|
||||
"email": tokenStorage.Email,
|
||||
"email": email,
|
||||
"api_key": tokenStorage.APIKey,
|
||||
"access_token": tokenStorage.AccessToken,
|
||||
"refresh_token": tokenStorage.RefreshToken,
|
||||
|
||||
Reference in New Issue
Block a user