package windsurf import ( "context" "encoding/json" "fmt" "math/rand" "net/http" "net/url" "strings" "time" "github.com/imroc/req/v3" ) type AuthClient struct { Auth1BaseURL string SeatServiceBaseURL string CodeiumRegisterURL string FirebaseAPIKey string RequestTimeout time.Duration } type LoginResult struct { APIKey string `json:"api_key"` Name string `json:"name"` Email string `json:"email"` IDToken string `json:"id_token,omitempty"` RefreshToken string `json:"refresh_token,omitempty"` SessionToken string `json:"session_token,omitempty"` Auth1Token string `json:"auth1_token,omitempty"` APIServerURL string `json:"api_server_url,omitempty"` AuthMethod string `json:"auth_method"` ExpiresIn int `json:"expires_in,omitempty"` } type RefreshResult struct { IDToken string `json:"id_token"` RefreshToken string `json:"refresh_token"` ExpiresIn int `json:"expires_in"` } type RegisterResult struct { APIKey string `json:"api_key"` Name string `json:"name"` APIServerURL string `json:"api_server_url"` } type AuthError struct { Message string IsAuthFail bool FirebaseCode string } func (e *AuthError) Error() string { return e.Message } var ( osVersions = []string{ "Windows NT 10.0; Win64; x64", "Macintosh; Intel Mac OS X 10_15_7", "Macintosh; Intel Mac OS X 13_4_1", "Macintosh; Intel Mac OS X 14_2_1", "X11; Linux x86_64", } chromeVersions = []string{ "120.0.0.0", "122.0.0.0", "124.0.0.0", "126.0.0.0", "128.0.0.0", "130.0.0.0", "132.0.0.0", "134.0.0.0", } acceptLanguages = []string{ "en-US,en;q=0.9", "zh-CN,zh;q=0.9,en;q=0.8", "ja,en-US;q=0.9,en;q=0.8", "de,en-US;q=0.9,en;q=0.8", } ) func pick(arr []string) string { return arr[rand.Intn(len(arr))] } func generateFingerprint() http.Header { os := pick(osVersions) cv := pick(chromeVersions) major := strings.Split(cv, ".")[0] ua := fmt.Sprintf("Mozilla/5.0 (%s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36", os, cv) h := http.Header{} h.Set("User-Agent", ua) h.Set("Accept-Language", pick(acceptLanguages)) h.Set("Accept", "application/json, text/plain, */*") h.Set("Accept-Encoding", "identity") h.Set("sec-ch-ua", fmt.Sprintf(`"Chromium";v="%s", "Google Chrome";v="%s", "Not-A.Brand";v="99"`, major, major)) h.Set("sec-ch-ua-mobile", "?0") if strings.Contains(os, "Windows") { h.Set("sec-ch-ua-platform", `"Windows"`) } else if strings.Contains(os, "Mac") { h.Set("sec-ch-ua-platform", `"macOS"`) } else { h.Set("sec-ch-ua-platform", `"Linux"`) } h.Set("Sec-Fetch-Dest", "empty") h.Set("Sec-Fetch-Mode", "cors") h.Set("Sec-Fetch-Site", "cross-site") h.Set("Origin", "https://windsurf.com") h.Set("Referer", "https://windsurf.com/") return h } func newClient(timeout time.Duration, proxyURL string) *req.Client { c := req.C().SetTimeout(timeout).ImpersonateChrome() if proxyURL != "" { c.SetProxyURL(proxyURL) } return c } func (a *AuthClient) Login(ctx context.Context, email, password, proxyURL string) (*LoginResult, error) { fp := generateFingerprint() connData, _ := a.fetchAuth1Connections(ctx, email, fp, proxyURL) authMethod, _ := extractString(connData, "auth_method", "method") if authMethod == "auth1" { hasPassword, _ := extractBool(connData, "auth_method", "has_password") if !hasPassword { return nil, &AuthError{ Message: "该账号未设置密码登录方式", IsAuthFail: true, } } return a.loginViaAuth1(ctx, email, password, fp, proxyURL) } result, fbErr := a.loginViaFirebase(ctx, email, password, fp, proxyURL) if fbErr == nil { return result, nil } if ae, ok := fbErr.(*AuthError); ok && ae.IsAuthFail { result2, a1Err := a.loginViaAuth1(ctx, email, password, fp, proxyURL) if a1Err == nil { return result2, nil } if ae2, ok2 := a1Err.(*AuthError); ok2 && ae2.IsAuthFail { return nil, fbErr } return nil, a1Err } return nil, fbErr } func (a *AuthClient) fetchAuth1Connections(ctx context.Context, email string, fp http.Header, proxyURL string) (map[string]any, error) { body := map[string]string{"product": "windsurf", "email": email} var result map[string]any c := newClient(a.RequestTimeout, proxyURL) resp, err := c.R().SetContext(ctx).SetHeaders(headerMap(fp)).SetBody(body).SetSuccessResult(&result).Post(a.Auth1BaseURL + "/_devin-auth/connections") if err != nil { return nil, err } if resp.IsErrorState() { return nil, fmt.Errorf("auth1 connections: status %d", resp.StatusCode) } return result, nil } func (a *AuthClient) loginViaAuth1(ctx context.Context, email, password string, fp http.Header, proxyURL string) (*LoginResult, error) { c := newClient(a.RequestTimeout, proxyURL) var loginResp map[string]any resp, err := c.R().SetContext(ctx).SetHeaders(headerMap(fp)). SetBody(map[string]string{"email": email, "password": password}). SetSuccessResult(&loginResp). Post(a.Auth1BaseURL + "/_devin-auth/password/login") if err != nil { return nil, fmt.Errorf("auth1 login: %w", err) } if resp.IsErrorState() || loginResp["detail"] != nil { detail, _ := loginResp["detail"].(string) return nil, classifyAuthError("Auth1 登录失败", detail) } auth1Token, _ := loginResp["token"].(string) if auth1Token == "" { return nil, fmt.Errorf("auth1 login: no token in response") } hdrs := headerMap(fp) hdrs["Connect-Protocol-Version"] = "1" var bridgeResp map[string]any resp, err = c.R().SetContext(ctx).SetHeaders(hdrs). SetBody(map[string]string{"auth1Token": auth1Token, "orgId": ""}). SetSuccessResult(&bridgeResp). Post(a.SeatServiceBaseURL + "/WindsurfPostAuth") if err != nil { return nil, fmt.Errorf("windsurf post auth: %w", err) } if resp.IsErrorState() { return nil, fmt.Errorf("windsurf post auth: status %d", resp.StatusCode) } sessionToken, _ := bridgeResp["sessionToken"].(string) if sessionToken == "" { return nil, fmt.Errorf("windsurf post auth: no sessionToken") } var ottResp map[string]any resp, err = c.R().SetContext(ctx).SetHeaders(hdrs). SetBody(map[string]string{"authToken": sessionToken}). SetSuccessResult(&ottResp). Post(a.SeatServiceBaseURL + "/GetOneTimeAuthToken") if err != nil { return nil, fmt.Errorf("get one-time token: %w", err) } if resp.IsErrorState() { return nil, fmt.Errorf("get one-time token: status %d", resp.StatusCode) } oneTimeToken, _ := ottResp["authToken"].(string) if oneTimeToken == "" { return nil, fmt.Errorf("get one-time token: no authToken") } reg, err := a.RegisterWithCodeium(ctx, oneTimeToken, fp, proxyURL) if err != nil { return nil, fmt.Errorf("codeium register (auth1): %w", err) } return &LoginResult{ APIKey: reg.APIKey, Name: reg.Name, Email: email, APIServerURL: reg.APIServerURL, SessionToken: sessionToken, Auth1Token: auth1Token, AuthMethod: "auth1", }, nil } func (a *AuthClient) loginViaFirebase(ctx context.Context, email, password string, fp http.Header, proxyURL string) (*LoginResult, error) { c := newClient(a.RequestTimeout, proxyURL) firebaseURL := fmt.Sprintf("https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=%s", a.FirebaseAPIKey) body := map[string]any{"email": email, "password": password, "returnSecureToken": true} var fbResp map[string]any resp, err := c.R().SetContext(ctx).SetHeaders(headerMap(fp)).SetBody(body).SetSuccessResult(&fbResp).Post(firebaseURL) if err != nil { return nil, fmt.Errorf("firebase login: %w", err) } if errObj, ok := fbResp["error"].(map[string]any); ok { msg, _ := errObj["message"].(string) return nil, classifyAuthError("Firebase 登录失败", msg) } if resp.IsErrorState() { return nil, fmt.Errorf("firebase login: status %d", resp.StatusCode) } idToken, _ := fbResp["idToken"].(string) if idToken == "" { return nil, fmt.Errorf("firebase login: no idToken") } refreshToken, _ := fbResp["refreshToken"].(string) reg, err := a.RegisterWithCodeium(ctx, idToken, fp, proxyURL) if err != nil { return nil, fmt.Errorf("codeium register (firebase): %w", err) } return &LoginResult{ APIKey: reg.APIKey, Name: reg.Name, Email: email, IDToken: idToken, RefreshToken: refreshToken, APIServerURL: reg.APIServerURL, AuthMethod: "firebase", }, nil } func (a *AuthClient) RegisterWithCodeium(ctx context.Context, token string, fp http.Header, proxyURL string) (*RegisterResult, error) { c := newClient(a.RequestTimeout, proxyURL) body := map[string]string{"firebase_id_token": token} var regResp map[string]any resp, err := c.R().SetContext(ctx).SetHeaders(headerMap(fp)).SetBody(body).SetSuccessResult(®Resp).Post(a.CodeiumRegisterURL) if err != nil { return nil, err } if resp.IsErrorState() { data, _ := json.Marshal(regResp) return nil, fmt.Errorf("codeium register: status %d: %s", resp.StatusCode, string(data)) } apiKey, _ := regResp["api_key"].(string) if apiKey == "" { return nil, fmt.Errorf("codeium register: no api_key in response") } name, _ := regResp["name"].(string) apiServerURL, _ := regResp["api_server_url"].(string) return &RegisterResult{APIKey: apiKey, Name: name, APIServerURL: apiServerURL}, nil } func (a *AuthClient) RefreshFirebaseToken(ctx context.Context, refreshToken, proxyURL string) (*RefreshResult, error) { if refreshToken == "" { return nil, fmt.Errorf("no refresh token available") } refreshURL := fmt.Sprintf("https://securetoken.googleapis.com/v1/token?key=%s", a.FirebaseAPIKey) postBody := fmt.Sprintf("grant_type=refresh_token&refresh_token=%s", url.QueryEscape(refreshToken)) c := newClient(a.RequestTimeout, proxyURL) var result map[string]any resp, err := c.R().SetContext(ctx). SetHeader("Content-Type", "application/x-www-form-urlencoded"). SetHeader("Referer", "https://windsurf.com/"). SetHeader("Origin", "https://windsurf.com"). SetBodyString(postBody). SetSuccessResult(&result). Post(refreshURL) if err != nil { return nil, fmt.Errorf("firebase refresh: %w", err) } if resp.IsErrorState() { if errObj, ok := result["error"].(map[string]any); ok { msg, _ := errObj["message"].(string) return nil, fmt.Errorf("firebase refresh: %s", msg) } return nil, fmt.Errorf("firebase refresh: status %d", resp.StatusCode) } idToken := firstString(result, "id_token", "idToken") if idToken == "" { return nil, fmt.Errorf("firebase refresh: no idToken in response") } newRefresh := firstString(result, "refresh_token", "refreshToken") if newRefresh == "" { newRefresh = refreshToken } expiresIn := 3600 if v, ok := result["expires_in"].(string); ok { fmt.Sscanf(v, "%d", &expiresIn) } else if v, ok := result["expiresIn"].(string); ok { fmt.Sscanf(v, "%d", &expiresIn) } return &RefreshResult{IDToken: idToken, RefreshToken: newRefresh, ExpiresIn: expiresIn}, nil } func (a *AuthClient) ReRegisterWithCodeium(ctx context.Context, idToken, proxyURL string) (*RegisterResult, error) { fp := generateFingerprint() return a.RegisterWithCodeium(ctx, idToken, fp, proxyURL) } func classifyAuthError(prefix, detail string) *AuthError { authFails := map[string]bool{ "EMAIL_NOT_FOUND": true, "INVALID_PASSWORD": true, "INVALID_LOGIN_CREDENTIALS": true, "Invalid email or password": true, "No password set. Please log in with Google or GitHub.": true, "No password set": true, } friendly := map[string]string{ "EMAIL_NOT_FOUND": "该邮箱未注册", "INVALID_PASSWORD": "密码错误", "INVALID_LOGIN_CREDENTIALS": "邮箱或密码错误", "Invalid email or password": "邮箱或密码错误", "USER_DISABLED": "账号已被停用", "TOO_MANY_ATTEMPTS_TRY_LATER": "尝试太多次,请稍后再试", "INVALID_EMAIL": "邮箱格式错误", } msg := detail if f, ok := friendly[detail]; ok { msg = f } return &AuthError{ Message: fmt.Sprintf("%s: %s", prefix, msg), IsAuthFail: authFails[detail], FirebaseCode: detail, } } func headerMap(h http.Header) map[string]string { m := make(map[string]string, len(h)) for k := range h { m[k] = h.Get(k) } return m } func extractString(data map[string]any, keys ...string) (string, bool) { current := data for i, k := range keys { if i == len(keys)-1 { v, ok := current[k].(string) return v, ok } next, ok := current[k].(map[string]any) if !ok { return "", false } current = next } return "", false } func extractBool(data map[string]any, keys ...string) (bool, bool) { current := data for i, k := range keys { if i == len(keys)-1 { v, ok := current[k].(bool) return v, ok } next, ok := current[k].(map[string]any) if !ok { return false, false } current = next } return false, false } func firstString(m map[string]any, keys ...string) string { for _, k := range keys { if v, ok := m[k].(string); ok && v != "" { return v } } return "" }