Spaces:
Runtime error
Runtime error
| package tools | |
| import ( | |
| "WarpGPT/pkg/env" | |
| "WarpGPT/pkg/funcaptcha" | |
| "WarpGPT/pkg/logger" | |
| "encoding/json" | |
| "fmt" | |
| http "github.com/bogdanfinn/fhttp" | |
| tls_client "github.com/bogdanfinn/tls-client" | |
| "github.com/bogdanfinn/tls-client/profiles" | |
| "io" | |
| "net/url" | |
| "os" | |
| "regexp" | |
| "strings" | |
| ) | |
| type Error struct { | |
| Location string | |
| StatusCode int | |
| Details string | |
| Error error | |
| } | |
| func NewError(location string, statusCode int, details string, err error) *Error { | |
| return &Error{ | |
| Location: location, | |
| StatusCode: statusCode, | |
| Details: details, | |
| Error: err, | |
| } | |
| } | |
| type Authenticator struct { | |
| EmailAddress string | |
| Password string | |
| Proxy string | |
| Session tls_client.HttpClient | |
| UserAgent string | |
| State string | |
| URL string | |
| PUID string | |
| Verifier_code string | |
| Verifier_challenge string | |
| AuthResult AuthResult | |
| } | |
| type ArkoseToken struct { | |
| Token string `json:"token"` | |
| ChallengeURL string `json:"challenge_url"` | |
| ChallengeURLCDN string `json:"challenge_url_cdn"` | |
| ChallengeURLCDNSRI *string `json:"challenge_url_cdn_sri"` | |
| } | |
| type AuthResult struct { | |
| AccessToken map[string]interface{} `json:"access_token"` | |
| PUID string `json:"puid"` | |
| FreshToken string `json:"fresh_token"` | |
| Model map[string]interface{} `json:"model"` | |
| } | |
| func NewAuthenticator(emailAddress, password string, puid string) *Authenticator { | |
| auth := &Authenticator{ | |
| EmailAddress: emailAddress, | |
| Password: password, | |
| Proxy: os.Getenv("proxy"), | |
| PUID: puid, | |
| UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36", | |
| } | |
| jar := tls_client.NewCookieJar() | |
| cookie := &http.Cookie{ | |
| Name: "_puid", | |
| Value: puid, | |
| Path: "/", | |
| Domain: ".openai.com", | |
| } | |
| urls, _ := url.Parse("https://openai.com") | |
| jar.SetCookies(urls, []*http.Cookie{cookie}) | |
| options := []tls_client.HttpClientOption{ | |
| tls_client.WithTimeoutSeconds(20), | |
| tls_client.WithClientProfile(profiles.Chrome_109), | |
| tls_client.WithNotFollowRedirects(), | |
| tls_client.WithCookieJar(jar), | |
| tls_client.WithProxyUrl(env.E.Proxy), | |
| } | |
| auth.Session, _ = tls_client.NewHttpClient(tls_client.NewNoopLogger(), options...) | |
| return auth | |
| } | |
| func (auth *Authenticator) URLEncode(str string) string { | |
| return url.QueryEscape(str) | |
| } | |
| func (auth *Authenticator) Begin() *Error { | |
| logger.Log.Debug("Auth Begin") | |
| target := "https://" + env.E.OpenaiHost + "/api/auth/csrf" | |
| req, err := http.NewRequest("GET", target, nil) | |
| if err != nil { | |
| return NewError("begin", 0, "", err) | |
| } | |
| req.Header.Set("Host", ""+env.E.OpenaiHost+"") | |
| req.Header.Set("Accept", "*/*") | |
| req.Header.Set("Connection", "keep-alive") | |
| req.Header.Set("User-Agent", auth.UserAgent) | |
| req.Header.Set("Accept-Language", "en-GB,en-US;q=0.9,en;q=0.8") | |
| req.Header.Set("Referer", "https://"+env.E.OpenaiHost+"/auth/login") | |
| req.Header.Set("Accept-Encoding", "gzip, deflate, br") | |
| resp, err := auth.Session.Do(req) | |
| if err != nil { | |
| return NewError("begin", 0, "", err) | |
| } | |
| defer resp.Body.Close() | |
| body, err := io.ReadAll(resp.Body) | |
| if err != nil { | |
| return NewError("begin", 0, "", err) | |
| } | |
| if resp.StatusCode == 200 && strings.Contains(resp.Header.Get("Content-Type"), "json") { | |
| var csrfTokenResponse struct { | |
| CsrfToken string `json:"csrfToken"` | |
| } | |
| err = json.Unmarshal(body, &csrfTokenResponse) | |
| if err != nil { | |
| return NewError("begin", 0, "", err) | |
| } | |
| csrfToken := csrfTokenResponse.CsrfToken | |
| return auth.partOne(csrfToken) | |
| } else { | |
| err := NewError("begin", resp.StatusCode, string(body), fmt.Errorf("error: Check details")) | |
| return err | |
| } | |
| } | |
| func (auth *Authenticator) partOne(csrfToken string) *Error { | |
| logger.Log.Debug("Auth One") | |
| auth_url := "https://" + env.E.OpenaiHost + "/api/auth/signin/auth0?prompt=login" | |
| headers := map[string]string{ | |
| "Host": "" + env.E.OpenaiHost + "", | |
| "User-Agent": auth.UserAgent, | |
| "Content-Type": "application/x-www-form-urlencoded", | |
| "Accept": "*/*", | |
| "Sec-Gpc": "1", | |
| "Accept-Language": "en-US,en;q=0.8", | |
| "Origin": "https://" + env.E.OpenaiHost + "", | |
| "Sec-Fetch-Site": "same-origin", | |
| "Sec-Fetch-Mode": "cors", | |
| "Sec-Fetch-Dest": "empty", | |
| "Referer": "https://" + env.E.OpenaiHost + "/auth/login", | |
| "Accept-Encoding": "gzip, deflate", | |
| } | |
| // Construct payload | |
| payload := fmt.Sprintf("callbackUrl=%%2F&csrfToken=%s&json=true", csrfToken) | |
| req, _ := http.NewRequest("POST", auth_url, strings.NewReader(payload)) | |
| for k, v := range headers { | |
| req.Header.Set(k, v) | |
| } | |
| resp, err := auth.Session.Do(req) | |
| if err != nil { | |
| return NewError("part_one", 0, "Failed to send request", err) | |
| } | |
| defer resp.Body.Close() | |
| body, err := io.ReadAll(resp.Body) | |
| if err != nil { | |
| return NewError("part_one", 0, "Failed to read requestbody", err) | |
| } | |
| if resp.StatusCode == 200 && strings.Contains(resp.Header.Get("Content-Type"), "json") { | |
| var urlResponse struct { | |
| URL string `json:"url"` | |
| } | |
| err = json.Unmarshal(body, &urlResponse) | |
| if err != nil { | |
| return NewError("part_one", 0, "Failed to decode JSON", err) | |
| } | |
| if urlResponse.URL == "https://"+env.E.OpenaiHost+"/api/auth/error?error=OAuthSignin" || strings.Contains(urlResponse.URL, "error") { | |
| err := NewError("part_one", resp.StatusCode, "You have been rate limited. Please try again later.", fmt.Errorf("error: Check details")) | |
| return err | |
| } | |
| return auth.partTwo(urlResponse.URL) | |
| } else { | |
| return NewError("part_one", resp.StatusCode, string(body), fmt.Errorf("error: Check details")) | |
| } | |
| } | |
| func (auth *Authenticator) partTwo(target string) *Error { | |
| logger.Log.Debug("Auth Two") | |
| headers := map[string]string{ | |
| "Host": "auth0.openai.com", | |
| "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", | |
| "Connection": "keep-alive", | |
| "User-Agent": auth.UserAgent, | |
| "Accept-Language": "en-US,en;q=0.9", | |
| "Referer": "https://chat.openai.com/", | |
| "Sec-Ch-Ua": "\"Not A(Brand\";v=\"99\", \"Google Chrome\";v=\"121\", \"Chromium\";v=\"121\"", | |
| "Sec-Ch-Ua-Arch": "\"x86\"", | |
| "Sec-Ch-Ua-Bitness": "\"64\"", | |
| "Sec-Ch-Ua-Full-Version": "\"121.0.6167.161\"", | |
| "Sec-Ch-Ua-Full-Version-List": "\"Not A(Brand\";v=\"99.0.0.0\", \"Google Chrome\";v=\"121.0.6167.161\", \"Chromium\";v=\"121.0.6167.161\"", | |
| } | |
| req, _ := http.NewRequest("GET", target, nil) | |
| for k, v := range headers { | |
| req.Header.Set(k, v) | |
| } | |
| resp, err := auth.Session.Do(req) | |
| if err != nil { | |
| return NewError("part_two", 0, "Failed to make request", err) | |
| } | |
| defer resp.Body.Close() | |
| body, _ := io.ReadAll(resp.Body) | |
| if resp.StatusCode == 302 || resp.StatusCode == 200 { | |
| stateRegex := regexp.MustCompile(`state=(.*)`) | |
| stateMatch := stateRegex.FindStringSubmatch(string(body)) | |
| if len(stateMatch) < 2 { | |
| return NewError("part_two", 0, "Could not find state in response", fmt.Errorf("error: Check details")) | |
| } | |
| state := strings.Split(stateMatch[1], `"`)[0] | |
| return auth.partThree(state) | |
| } else { | |
| return NewError("part_two", resp.StatusCode, string(body), fmt.Errorf("error: Check details")) | |
| } | |
| } | |
| func (auth *Authenticator) partThree(state string) *Error { | |
| logger.Log.Debug("Auth Three") | |
| target := fmt.Sprintf("https://auth0.openai.com/u/login/identifier?state=%s", state) | |
| emailURLEncoded := auth.URLEncode(auth.EmailAddress) | |
| payload := fmt.Sprintf( | |
| "state=%s&username=%s&js-available=false&webauthn-available=true&is-brave=false&webauthn-platform-available=true&action=default", | |
| state, emailURLEncoded, | |
| ) | |
| headers := map[string]string{ | |
| "Host": "auth0.openai.com", | |
| "Origin": "https://auth0.openai.com", | |
| "Connection": "keep-alive", | |
| "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", | |
| "User-Agent": auth.UserAgent, | |
| "Referer": fmt.Sprintf("https://auth0.openai.com/u/login/identifier?state=%s", state), | |
| "Accept-Language": "en-US,en;q=0.9", | |
| "Content-Type": "application/x-www-form-urlencoded", | |
| } | |
| req, _ := http.NewRequest("POST", target, strings.NewReader(payload)) | |
| for k, v := range headers { | |
| req.Header.Set(k, v) | |
| } | |
| resp, err := auth.Session.Do(req) | |
| if err != nil { | |
| return NewError("part_three", 0, "Failed to send request", err) | |
| } | |
| defer resp.Body.Close() | |
| if resp.StatusCode == 302 || resp.StatusCode == 200 { | |
| return auth.partFour(state) | |
| } else { | |
| return NewError("part_three", resp.StatusCode, "Your email address is invalid.", fmt.Errorf("error: Check details")) | |
| } | |
| } | |
| func (auth *Authenticator) partFour(state string) *Error { | |
| logger.Log.Debug("Auth Four") | |
| target := fmt.Sprintf("https://auth0.openai.com/u/login/password?state=%s", state) | |
| emailURLEncoded := auth.URLEncode(auth.EmailAddress) | |
| passwordURLEncoded := auth.URLEncode(auth.Password) | |
| payload := fmt.Sprintf("state=%s&username=%s&password=%s", state, emailURLEncoded, passwordURLEncoded) | |
| headers := map[string]string{ | |
| "Host": "auth0.openai.com", | |
| "Origin": "https://auth0.openai.com", | |
| "Connection": "keep-alive", | |
| "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", | |
| "User-Agent": auth.UserAgent, | |
| "Referer": fmt.Sprintf("https://auth0.openai.com/u/login/password?state=%s", state), | |
| "Accept-Language": "en-US,en;q=0.9", | |
| "Content-Type": "application/x-www-form-urlencoded", | |
| } | |
| req, _ := http.NewRequest("POST", target, strings.NewReader(payload)) | |
| for k, v := range headers { | |
| req.Header.Set(k, v) | |
| } | |
| token, err := funcaptcha.GetOpenAIArkoseToken(0, auth.PUID) | |
| if err != nil { | |
| return NewError("part_four", 0, "get arkose_token failed", err) | |
| } | |
| cookie := &http.Cookie{ | |
| Name: "arkoseToken", | |
| Value: token, | |
| Path: "/", | |
| } | |
| req.AddCookie(cookie) | |
| resp, err := auth.Session.Do(req) | |
| if err != nil { | |
| return NewError("part_four", 0, "Failed to send request", err) | |
| } | |
| defer resp.Body.Close() | |
| if resp.StatusCode == 302 { | |
| redirectURL := resp.Header.Get("Location") | |
| println(redirectURL) | |
| return auth.partFive(state, redirectURL) | |
| } else { | |
| var body interface{} | |
| if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { | |
| return NewError("part_four", 0, "", err) | |
| } | |
| return NewError("part_four", resp.StatusCode, body.(string), fmt.Errorf("error: Check details")) | |
| } | |
| } | |
| func (auth *Authenticator) partFive(oldState string, redirectURL string) *Error { | |
| logger.Log.Debug("Auth Five") | |
| target := "https://auth0.openai.com" + redirectURL | |
| headers := map[string]string{ | |
| "Host": "auth0.openai.com", | |
| "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", | |
| "Connection": "keep-alive", | |
| "User-Agent": auth.UserAgent, | |
| "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8", | |
| "Referer": fmt.Sprintf("https://auth0.openai.com/u/login/password?state=%s", oldState), | |
| } | |
| req, _ := http.NewRequest("GET", target, nil) | |
| for k, v := range headers { | |
| req.Header.Set(k, v) | |
| } | |
| resp, err := auth.Session.Do(req) | |
| if err != nil { | |
| return NewError("part_five", 0, "Failed to send request", err) | |
| } | |
| defer resp.Body.Close() | |
| if resp.StatusCode == 302 { | |
| return auth.partSix(resp.Header.Get("Location"), target) | |
| } else { | |
| return NewError("part_five", resp.StatusCode, resp.Status, fmt.Errorf("error: Check details")) | |
| } | |
| } | |
| func (auth *Authenticator) partSix(urls, redirect_url string) *Error { | |
| logger.Log.Debug("Auth Six") | |
| req, _ := http.NewRequest("GET", urls, nil) | |
| for k, v := range map[string]string{ | |
| "Host": "" + env.E.OpenaiHost + "", | |
| "Accept": "application/json", | |
| "Connection": "keep-alive", | |
| "User-Agent": auth.UserAgent, | |
| "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8", | |
| "Referer": redirect_url, | |
| } { | |
| req.Header.Set(k, v) | |
| } | |
| resp, err := auth.Session.Do(req) | |
| if err != nil { | |
| return NewError("part_six", 0, "Failed to send request", err) | |
| } | |
| defer resp.Body.Close() | |
| if err != nil { | |
| return NewError("part_six", 0, "Response was not JSON", err) | |
| } | |
| if resp.StatusCode != 302 { | |
| return NewError("part_six", resp.StatusCode, urls, fmt.Errorf("incorrect response code")) | |
| } | |
| // Check location header | |
| if location := resp.Header.Get("Location"); location != "https://"+env.E.OpenaiHost+"/" { | |
| return NewError("part_six", resp.StatusCode, location, fmt.Errorf("incorrect redirect")) | |
| } | |
| sessionUrl := "https://" + env.E.OpenaiHost + "/api/auth/session" | |
| req, _ = http.NewRequest("GET", sessionUrl, nil) | |
| // Set user agent | |
| req.Header.Set("User-Agent", auth.UserAgent) | |
| resp, err = auth.Session.Do(req) | |
| if err != nil { | |
| return NewError("get_access_token", 0, "Failed to send request", err) | |
| } | |
| if resp.StatusCode != 200 { | |
| return NewError("get_access_token", resp.StatusCode, "Incorrect response code", fmt.Errorf("error: Check details")) | |
| } | |
| var result map[string]interface{} | |
| if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { | |
| return NewError("get_access_token", 0, "", err) | |
| } | |
| // Check if access token in data | |
| if _, ok := result["accessToken"]; !ok { | |
| resultString := fmt.Sprintf("%v", result) | |
| return NewError("part_six", 0, resultString, fmt.Errorf("missing access token")) | |
| } | |
| cookieUrl, _ := url.Parse("https://" + env.E.OpenaiHost + "") | |
| jar := auth.Session.GetCookies(cookieUrl) | |
| auth.AuthResult.AccessToken = result | |
| for _, cookie := range jar { | |
| if cookie.Name == "__Secure-next-auth.session-token" { | |
| auth.AuthResult.FreshToken = cookie.Value | |
| } | |
| } | |
| return nil | |
| } | |
| func (auth *Authenticator) GetAccessTokenByRefreshToken(freshToken string) *Error { | |
| logger.Log.Debug("GetAccessTokenByRefreshToken") | |
| sessionUrl := "https://" + env.E.OpenaiHost + "/api/auth/session" | |
| req, _ := http.NewRequest("GET", sessionUrl, nil) | |
| cookies := &http.Cookie{ | |
| Name: "__Secure-next-auth.session-token", | |
| Value: freshToken, | |
| } | |
| req.AddCookie(cookies) | |
| // Set user agent | |
| req.Header.Set("User-Agent", auth.UserAgent) | |
| resp, err := auth.Session.Do(req) | |
| if err != nil { | |
| return NewError("GetAccessTokenByRefreshToken", 0, "Failed to send request", err) | |
| } | |
| if resp.StatusCode != 200 { | |
| return NewError("GetAccessTokenByRefreshToken", resp.StatusCode, "Incorrect response code", fmt.Errorf("error: Check details")) | |
| } | |
| var result map[string]interface{} | |
| if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { | |
| return NewError("GetAccessTokenByRefreshToken", 0, "", err) | |
| } | |
| // Check if access token in data | |
| if _, ok := result["accessToken"]; !ok { | |
| resultString := fmt.Sprintf("%v", result) | |
| return NewError("GetAccessTokenByRefreshToken", 0, resultString, fmt.Errorf("missing access token")) | |
| } | |
| cookieUrl, _ := url.Parse("https://" + env.E.OpenaiHost + "") | |
| jar := auth.Session.GetCookies(cookieUrl) | |
| auth.AuthResult.AccessToken = result | |
| for _, cookie := range jar { | |
| if cookie.Name == "__Secure-next-auth.session-token" { | |
| auth.AuthResult.FreshToken = cookie.Value | |
| } | |
| } | |
| return nil | |
| } | |
| func (auth *Authenticator) GetAccessToken() map[string]interface{} { | |
| logger.Log.Debug("GetAccessToken") | |
| return auth.AuthResult.AccessToken | |
| } | |
| func (auth *Authenticator) GetRefreshToken() string { | |
| logger.Log.Debug("GetRefreshToken") | |
| return auth.AuthResult.FreshToken | |
| } | |
| func (auth *Authenticator) GetModels() (map[string]interface{}, *Error) { | |
| logger.Log.Debug("GetModels") | |
| if len(auth.AuthResult.AccessToken) == 0 { | |
| return nil, NewError("get_model", 0, "Missing access token", fmt.Errorf("error: Check details")) | |
| } | |
| // Make request to https://"+common.E.OpenAI_HOST+"/backend-api/models | |
| req, _ := http.NewRequest("GET", "https://"+env.E.OpenaiHost+"/backend-api/models", nil) | |
| // Add headers | |
| req.Header.Add("Authorization", "Bearer "+auth.AuthResult.AccessToken["accessToken"].(string)) | |
| req.Header.Add("User-Agent", auth.UserAgent) | |
| req.Header.Add("Accept", "application/json") | |
| req.Header.Add("Accept-Language", "en-US,en;q=0.9") | |
| req.Header.Add("Referer", "https://"+env.E.OpenaiHost+"/") | |
| req.Header.Add("Origin", "https://"+env.E.OpenaiHost+"") | |
| req.Header.Add("Connection", "keep-alive") | |
| resp, err := auth.Session.Do(req) | |
| if err != nil { | |
| return nil, NewError("get_model", 0, "Failed to make request", err) | |
| } | |
| defer resp.Body.Close() | |
| if resp.StatusCode != 200 { | |
| return nil, NewError("get_model", resp.StatusCode, "Failed to make request", fmt.Errorf("error: Check details")) | |
| } | |
| var responseBody map[string]interface{} | |
| r, err := io.ReadAll(resp.Body) | |
| if err != nil { | |
| return nil, NewError("get_model", resp.StatusCode, "Failed to get response", fmt.Errorf("error: Check details")) | |
| } | |
| if err := json.Unmarshal(r, &responseBody); err != nil { | |
| return nil, NewError("get_model", resp.StatusCode, "Failed to get response", fmt.Errorf("error: Check details")) | |
| } | |
| auth.AuthResult.Model = responseBody | |
| return responseBody, nil | |
| } | |
| func (auth *Authenticator) GetPUID() (string, *Error) { | |
| logger.Log.Debug("GetPUID") | |
| // Check if user has access token | |
| if len(auth.AuthResult.AccessToken) == 0 { | |
| return "", NewError("get_puid", 0, "Missing access token", fmt.Errorf("error: Check details")) | |
| } | |
| // Make request to https://"+common.E.OpenAI_HOST+"/backend-api/models | |
| req, _ := http.NewRequest("GET", "https://"+env.E.OpenaiHost+"/backend-api/models", nil) | |
| // Add headers | |
| req.Header.Add("Authorization", "Bearer "+auth.AuthResult.AccessToken["accessToken"].(string)) | |
| req.Header.Add("User-Agent", auth.UserAgent) | |
| req.Header.Add("Accept", "application/json") | |
| req.Header.Add("Accept-Language", "en-US,en;q=0.9") | |
| req.Header.Add("Referer", "https://"+env.E.OpenaiHost+"/") | |
| req.Header.Add("Origin", "https://"+env.E.OpenaiHost+"") | |
| req.Header.Add("Connection", "keep-alive") | |
| resp, err := auth.Session.Do(req) | |
| if err != nil { | |
| return "", NewError("get_puid", 0, "Failed to make request", err) | |
| } | |
| defer resp.Body.Close() | |
| if resp.StatusCode != 200 { | |
| return "", NewError("get_puid", resp.StatusCode, "Failed to make request", fmt.Errorf("error: Check details")) | |
| } | |
| // Find `_puid` cookie in response | |
| for _, cookie := range resp.Cookies() { | |
| if cookie.Name == "_puid" { | |
| auth.AuthResult.PUID = cookie.Value | |
| return cookie.Value, nil | |
| } | |
| } | |
| // If cookie not found, return error | |
| return "", NewError("get_puid", 0, "PUID cookie not found", fmt.Errorf("error: Check details")) | |
| } | |
| func (auth *Authenticator) GetAuthResult() AuthResult { | |
| logger.Log.Debug("GetAuthResult") | |
| return auth.AuthResult | |
| } | |