History

Merge branch 'router-for-me:main' into main

Luis Pater committed 11 months agov6.6.7-0

Commit ba6aa5f2 parents 75ce091 1c52a895 files · +97 −5Browse files

Unified · Split

5 files
  1. internal/api/handlers/management/auth_files.go +14 −2
  2. internal/auth/iflow/cookie_helpers.go +61 −0
  3. internal/auth/iflow/iflow_auth.go +8 −1
  4. internal/cmd/iflow_cookie.go +13 −1
  5. sdk/auth/iflow.go +1 −1
internal/api/handlers/management/auth_files.go+14−2
@@ -1745,6 +1745,17 @@ func (h *Handler) RequestIFlowCookieToken(c *gin.Context) { · 1744 unmodified lines
1745 return1745 return
1746 }1746 }
1747 1747
1748 // Check for duplicate BXAuth before authentication
1749 bxAuth := iflowauth.ExtractBXAuth(cookieValue)
1750 if existingFile, err := iflowauth.CheckDuplicateBXAuth(h.cfg.AuthDir, bxAuth); err != nil {
1751 c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "error": "failed to check duplicate"})
1752 return
1753 } else if existingFile != "" {
1754 existingFileName := filepath.Base(existingFile)
1755 c.JSON(http.StatusConflict, gin.H{"status": "error", "error": "duplicate BXAuth found", "existing_file": existingFileName})
1756 return
1757 }
1758
1748 authSvc := iflowauth.NewIFlowAuth(h.cfg)1759 authSvc := iflowauth.NewIFlowAuth(h.cfg)
1749 tokenData, errAuth := authSvc.AuthenticateWithCookie(ctx, cookieValue)1760 tokenData, errAuth := authSvc.AuthenticateWithCookie(ctx, cookieValue)
1750 if errAuth != nil {1761 if errAuth != nil {
@@ -1767,11 +1778,12 @@ func (h *Handler) RequestIFlowCookieToken(c *gin.Context) { · 16 unmodified lines
1767 }1778 }
1768 1779
1769 tokenStorage.Email = email1780 tokenStorage.Email = email
1781 timestamp := time.Now().Unix()
1770 1782
1771 record := &coreauth.Auth{1783 record := &coreauth.Auth{
1772 ID: fmt.Sprintf("iflow-%s.json", fileName),1784 ID: fmt.Sprintf("iflow-%s-%d.json", fileName, timestamp),
1773 Provider: "iflow",1785 Provider: "iflow",
1774 FileName: fmt.Sprintf("iflow-%s.json", fileName),1786 FileName: fmt.Sprintf("iflow-%s-%d.json", fileName, timestamp),
1775 Storage: tokenStorage,1787 Storage: tokenStorage,
1776 Metadata: map[string]any{1788 Metadata: map[string]any{
1777 "email": email,1789 "email": email,
internal/auth/iflow/cookie_helpers.go+61−0
@@ -1,7 +1,10 @@
1package iflow1package iflow
2 2
3import (3import (
4 "encoding/json"
4 "fmt"5 "fmt"
6 "os"
7 "path/filepath"
5 "strings"8 "strings"
6)9)
7 10
@@ -36,3 +39,61 @@ func SanitizeIFlowFileName(raw string) string { · 28 unmodified lines
36 }39 }
37 return strings.TrimSpace(result.String())40 return strings.TrimSpace(result.String())
38}41}
42
43// ExtractBXAuth extracts the BXAuth value from a cookie string.
44func ExtractBXAuth(cookie string) string {
45 parts := strings.Split(cookie, ";")
46 for _, part := range parts {
47 part = strings.TrimSpace(part)
48 if strings.HasPrefix(part, "BXAuth=") {
49 return strings.TrimPrefix(part, "BXAuth=")
50 }
51 }
52 return ""
53}
54
55// CheckDuplicateBXAuth checks if the given BXAuth value already exists in any iflow auth file.
56// Returns the path of the existing file if found, empty string otherwise.
57func CheckDuplicateBXAuth(authDir, bxAuth string) (string, error) {
58 if bxAuth == "" {
59 return "", nil
60 }
61
62 entries, err := os.ReadDir(authDir)
63 if err != nil {
64 if os.IsNotExist(err) {
65 return "", nil
66 }
67 return "", fmt.Errorf("read auth dir failed: %w", err)
68 }
69
70 for _, entry := range entries {
71 if entry.IsDir() {
72 continue
73 }
74 name := entry.Name()
75 if !strings.HasPrefix(name, "iflow-") || !strings.HasSuffix(name, ".json") {
76 continue
77 }
78
79 filePath := filepath.Join(authDir, name)
80 data, err := os.ReadFile(filePath)
81 if err != nil {
82 continue
83 }
84
85 var tokenData struct {
86 Cookie string `json:"cookie"`
87 }
88 if err := json.Unmarshal(data, &tokenData); err != nil {
89 continue
90 }
91
92 existingBXAuth := ExtractBXAuth(tokenData.Cookie)
93 if existingBXAuth != "" && existingBXAuth == bxAuth {
94 return filePath, nil
95 }
96 }
97
98 return "", nil
99}
internal/auth/iflow/iflow_auth.go+8−1
@@ -506,11 +506,18 @@ func (ia *IFlowAuth) CreateCookieTokenStorage(data *IFlowTokenData) *IFlowTokenS · 505 unmodified lines
506 return nil506 return nil
507 }507 }
508 508
509 // Only save the BXAuth field from the cookie
510 bxAuth := ExtractBXAuth(data.Cookie)
511 cookieToSave := ""
512 if bxAuth != "" {
513 cookieToSave = "BXAuth=" + bxAuth + ";"
514 }
515
509 return &IFlowTokenStorage{516 return &IFlowTokenStorage{
510 APIKey: data.APIKey,517 APIKey: data.APIKey,
511 Email: data.Email,518 Email: data.Email,
512 Expire: data.Expire,519 Expire: data.Expire,
513 Cookie: data.Cookie,520 Cookie: cookieToSave,
514 LastRefresh: time.Now().Format(time.RFC3339),521 LastRefresh: time.Now().Format(time.RFC3339),
515 Type: "iflow",522 Type: "iflow",
516 }523 }
internal/cmd/iflow_cookie.go+13−1
@@ -5,7 +5,9 @@ import ( · 4 unmodified lines
5 "context"5 "context"
6 "fmt"6 "fmt"
7 "os"7 "os"
8 "path/filepath"
8 "strings"9 "strings"
10 "time"
9 11
10 "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/iflow"12 "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/iflow"
11 "github.com/router-for-me/CLIProxyAPI/v6/internal/config"13 "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
@@ -37,6 +39,16 @@ func DoIFlowCookieAuth(cfg *config.Config, options *LoginOptions) { · 25 unmodified lines
37 return39 return
38 }40 }
39 41
42 // Check for duplicate BXAuth before authentication
43 bxAuth := iflow.ExtractBXAuth(cookie)
44 if existingFile, err := iflow.CheckDuplicateBXAuth(cfg.AuthDir, bxAuth); err != nil {
45 fmt.Printf("Failed to check duplicate: %v\n", err)
46 return
47 } else if existingFile != "" {
48 fmt.Printf("Duplicate BXAuth found, authentication already exists: %s\n", filepath.Base(existingFile))
49 return
50 }
51
40 // Authenticate with cookie52 // Authenticate with cookie
41 auth := iflow.NewIFlowAuth(cfg)53 auth := iflow.NewIFlowAuth(cfg)
42 ctx := context.Background()54 ctx := context.Background()
@@ -82,5 +94,5 @@ func promptForCookie(promptFn func(string) (string, error)) (string, error) { · 39 unmodified lines
82// getAuthFilePath returns the auth file path for the given provider and email94// getAuthFilePath returns the auth file path for the given provider and email
83func getAuthFilePath(cfg *config.Config, provider, email string) string {95func getAuthFilePath(cfg *config.Config, provider, email string) string {
84 fileName := iflow.SanitizeIFlowFileName(email)96 fileName := iflow.SanitizeIFlowFileName(email)
85 return fmt.Sprintf("%s/%s-%s.json", cfg.AuthDir, provider, fileName)97 return fmt.Sprintf("%s/%s-%s-%d.json", cfg.AuthDir, provider, fileName, time.Now().Unix())
86}98}
sdk/auth/iflow.go+1−1
@@ -107,7 +107,7 @@ func (a *IFlowAuthenticator) Login(ctx context.Context, cfg *config.Config, opts · 106 unmodified lines
107 return nil, fmt.Errorf("iflow authentication failed: missing account identifier")107 return nil, fmt.Errorf("iflow authentication failed: missing account identifier")
108 }108 }
109 109
110 fileName := fmt.Sprintf("iflow-%s.json", email)110 fileName := fmt.Sprintf("iflow-%s-%d.json", email, time.Now().Unix())
111 metadata := map[string]any{111 metadata := map[string]any{
112 "email": email,112 "email": email,
113 "api_key": tokenStorage.APIKey,113 "api_key": tokenStorage.APIKey,