mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 13:00:52 +08:00
fix(auth): prevent duplicate iflow BXAuth tokens
This commit is contained in:
@@ -1722,6 +1722,17 @@ func (h *Handler) RequestIFlowCookieToken(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for duplicate BXAuth before authentication
|
||||||
|
bxAuth := iflowauth.ExtractBXAuth(cookieValue)
|
||||||
|
if existingFile, err := iflowauth.CheckDuplicateBXAuth(h.cfg.AuthDir, bxAuth); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "error": "failed to check duplicate"})
|
||||||
|
return
|
||||||
|
} else if existingFile != "" {
|
||||||
|
existingFileName := filepath.Base(existingFile)
|
||||||
|
c.JSON(http.StatusConflict, gin.H{"status": "error", "error": "duplicate BXAuth found", "existing_file": existingFileName})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
authSvc := iflowauth.NewIFlowAuth(h.cfg)
|
authSvc := iflowauth.NewIFlowAuth(h.cfg)
|
||||||
tokenData, errAuth := authSvc.AuthenticateWithCookie(ctx, cookieValue)
|
tokenData, errAuth := authSvc.AuthenticateWithCookie(ctx, cookieValue)
|
||||||
if errAuth != nil {
|
if errAuth != nil {
|
||||||
@@ -1744,11 +1755,12 @@ func (h *Handler) RequestIFlowCookieToken(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tokenStorage.Email = email
|
tokenStorage.Email = email
|
||||||
|
timestamp := time.Now().Unix()
|
||||||
|
|
||||||
record := &coreauth.Auth{
|
record := &coreauth.Auth{
|
||||||
ID: fmt.Sprintf("iflow-%s.json", fileName),
|
ID: fmt.Sprintf("iflow-%s-%d.json", fileName, timestamp),
|
||||||
Provider: "iflow",
|
Provider: "iflow",
|
||||||
FileName: fmt.Sprintf("iflow-%s.json", fileName),
|
FileName: fmt.Sprintf("iflow-%s-%d.json", fileName, timestamp),
|
||||||
Storage: tokenStorage,
|
Storage: tokenStorage,
|
||||||
Metadata: map[string]any{
|
Metadata: map[string]any{
|
||||||
"email": email,
|
"email": email,
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package iflow
|
package iflow
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,3 +39,61 @@ func SanitizeIFlowFileName(raw string) string {
|
|||||||
}
|
}
|
||||||
return strings.TrimSpace(result.String())
|
return strings.TrimSpace(result.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExtractBXAuth extracts the BXAuth value from a cookie string.
|
||||||
|
func ExtractBXAuth(cookie string) string {
|
||||||
|
parts := strings.Split(cookie, ";")
|
||||||
|
for _, part := range parts {
|
||||||
|
part = strings.TrimSpace(part)
|
||||||
|
if strings.HasPrefix(part, "BXAuth=") {
|
||||||
|
return strings.TrimPrefix(part, "BXAuth=")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckDuplicateBXAuth checks if the given BXAuth value already exists in any iflow auth file.
|
||||||
|
// Returns the path of the existing file if found, empty string otherwise.
|
||||||
|
func CheckDuplicateBXAuth(authDir, bxAuth string) (string, error) {
|
||||||
|
if bxAuth == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(authDir)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("read auth dir failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := entry.Name()
|
||||||
|
if !strings.HasPrefix(name, "iflow-") || !strings.HasSuffix(name, ".json") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
filePath := filepath.Join(authDir, name)
|
||||||
|
data, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokenData struct {
|
||||||
|
Cookie string `json:"cookie"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(data, &tokenData); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
existingBXAuth := ExtractBXAuth(tokenData.Cookie)
|
||||||
|
if existingBXAuth != "" && existingBXAuth == bxAuth {
|
||||||
|
return filePath, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -494,11 +494,18 @@ func (ia *IFlowAuth) CreateCookieTokenStorage(data *IFlowTokenData) *IFlowTokenS
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only save the BXAuth field from the cookie
|
||||||
|
bxAuth := ExtractBXAuth(data.Cookie)
|
||||||
|
cookieToSave := ""
|
||||||
|
if bxAuth != "" {
|
||||||
|
cookieToSave = "BXAuth=" + bxAuth + ";"
|
||||||
|
}
|
||||||
|
|
||||||
return &IFlowTokenStorage{
|
return &IFlowTokenStorage{
|
||||||
APIKey: data.APIKey,
|
APIKey: data.APIKey,
|
||||||
Email: data.Email,
|
Email: data.Email,
|
||||||
Expire: data.Expire,
|
Expire: data.Expire,
|
||||||
Cookie: data.Cookie,
|
Cookie: cookieToSave,
|
||||||
LastRefresh: time.Now().Format(time.RFC3339),
|
LastRefresh: time.Now().Format(time.RFC3339),
|
||||||
Type: "iflow",
|
Type: "iflow",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/iflow"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/iflow"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||||
@@ -37,6 +39,16 @@ func DoIFlowCookieAuth(cfg *config.Config, options *LoginOptions) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for duplicate BXAuth before authentication
|
||||||
|
bxAuth := iflow.ExtractBXAuth(cookie)
|
||||||
|
if existingFile, err := iflow.CheckDuplicateBXAuth(cfg.AuthDir, bxAuth); err != nil {
|
||||||
|
fmt.Printf("Failed to check duplicate: %v\n", err)
|
||||||
|
return
|
||||||
|
} else if existingFile != "" {
|
||||||
|
fmt.Printf("Duplicate BXAuth found, authentication already exists: %s\n", filepath.Base(existingFile))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Authenticate with cookie
|
// Authenticate with cookie
|
||||||
auth := iflow.NewIFlowAuth(cfg)
|
auth := iflow.NewIFlowAuth(cfg)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
@@ -82,5 +94,5 @@ func promptForCookie(promptFn func(string) (string, error)) (string, error) {
|
|||||||
// getAuthFilePath returns the auth file path for the given provider and email
|
// getAuthFilePath returns the auth file path for the given provider and email
|
||||||
func getAuthFilePath(cfg *config.Config, provider, email string) string {
|
func getAuthFilePath(cfg *config.Config, provider, email string) string {
|
||||||
fileName := iflow.SanitizeIFlowFileName(email)
|
fileName := iflow.SanitizeIFlowFileName(email)
|
||||||
return fmt.Sprintf("%s/%s-%s.json", cfg.AuthDir, provider, fileName)
|
return fmt.Sprintf("%s/%s-%s-%d.json", cfg.AuthDir, provider, fileName, time.Now().Unix())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ func (a *IFlowAuthenticator) Login(ctx context.Context, cfg *config.Config, opts
|
|||||||
return nil, fmt.Errorf("iflow authentication failed: missing account identifier")
|
return nil, fmt.Errorf("iflow authentication failed: missing account identifier")
|
||||||
}
|
}
|
||||||
|
|
||||||
fileName := fmt.Sprintf("iflow-%s.json", email)
|
fileName := fmt.Sprintf("iflow-%s-%d.json", email, time.Now().Unix())
|
||||||
metadata := map[string]any{
|
metadata := map[string]any{
|
||||||
"email": email,
|
"email": email,
|
||||||
"api_key": tokenStorage.APIKey,
|
"api_key": tokenStorage.APIKey,
|
||||||
|
|||||||
Reference in New Issue
Block a user