1
0

tojson.sh

This commit is contained in:
Antonio Cheong 2023-07-31 13:01:53 +08:00
parent 0431ff8703
commit 6d1f0345ec
5 changed files with 472 additions and 28 deletions

View File

@ -0,0 +1,432 @@
package auth
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/url"
"regexp"
"strings"
http "github.com/bogdanfinn/fhttp"
tls_client "github.com/bogdanfinn/tls-client"
pkce "github.com/nirasan/go-oauth-pkce-code-verifier"
)
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
Verifier_code string
Verifier_challenge string
AuthResult AuthResult
}
type AuthResult struct {
AccessToken string `json:"access_token"`
PUID string `json:"puid"`
}
func NewAuthenticator(emailAddress, password, proxy string) *Authenticator {
auth := &Authenticator{
EmailAddress: emailAddress,
Password: password,
Proxy: proxy,
UserAgent: "ChatGPT/1.2023.187 (iOS 16.5.1; iPhone12,1; build 1744)",
}
jar := tls_client.NewCookieJar()
options := []tls_client.HttpClientOption{
tls_client.WithTimeoutSeconds(20),
tls_client.WithClientProfile(tls_client.Safari_IOS_16_0),
tls_client.WithNotFollowRedirects(),
tls_client.WithCookieJar(jar), // create cookieJar instance and pass it as argument
// Proxy
tls_client.WithProxyUrl(proxy),
}
auth.Session, _ = tls_client.NewHttpClient(tls_client.NewNoopLogger(), options...)
// PKCE
verifier, _ := pkce.CreateCodeVerifier()
auth.Verifier_code = verifier.String()
auth.Verifier_challenge = verifier.CodeChallengeS256()
return auth
}
func (auth *Authenticator) URLEncode(str string) string {
return url.QueryEscape(str)
}
func (auth *Authenticator) Begin() *Error {
url := "https://chat.openai.com/api/auth/csrf"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return NewError("begin", 0, "", err)
}
req.Header.Set("Host", "chat.openai.com")
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://chat.openai.com/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 {
auth_url := "https://chat.openai.com/api/auth/signin/auth0?prompt=login"
headers := map[string]string{
"Host": "chat.openai.com",
"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://chat.openai.com",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Dest": "empty",
"Referer": "https://chat.openai.com/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 body", 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://chat.openai.com/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(url string) *Error {
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://ios.chat.openai.com/",
}
req, _ := http.NewRequest("GET", url, 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 {
url := 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", 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_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 {
url := 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&action=default", 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,*/*;q=0.8",
"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", 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_four", 0, "Failed to send request", err)
}
defer resp.Body.Close()
if resp.StatusCode == 302 {
redirectURL := resp.Header.Get("Location")
return auth.partFive(state, redirectURL)
} else {
body := bytes.NewBuffer(nil)
body.ReadFrom(resp.Body)
return NewError("part_four", resp.StatusCode, body.String(), fmt.Errorf("error: Check details"))
}
}
func (auth *Authenticator) partFive(oldState string, redirectURL string) *Error {
url := "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", url, 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"), url)
} else {
return NewError("part_five", resp.StatusCode, resp.Status, fmt.Errorf("error: Check details"))
}
}
func (auth *Authenticator) partSix(url, redirect_url string) *Error {
req, _ := http.NewRequest("GET", url, nil)
for k, v := range map[string]string{
"Host": "chat.openai.com",
"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, url, fmt.Errorf("incorrect response code"))
}
// Check location header
if location := resp.Header.Get("Location"); location != "https://chat.openai.com/" {
return NewError("part_six", resp.StatusCode, location, fmt.Errorf("incorrect redirect"))
}
url = "https://chat.openai.com/api/auth/session"
req, _ = http.NewRequest("GET", url, 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 {
result_string := fmt.Sprintf("%v", result)
return NewError("part_six", 0, result_string, fmt.Errorf("missing access token"))
}
auth.AuthResult.AccessToken = result["accessToken"].(string)
return nil
}
func (auth *Authenticator) GetAccessToken() string {
return auth.AuthResult.AccessToken
}
func (auth *Authenticator) GetPUID() (string, *Error) {
// Check if user has access token
if auth.AuthResult.AccessToken == "" {
return "", NewError("get_puid", 0, "Missing access token", fmt.Errorf("error: Check details"))
}
// Make request to https://chat.openai.com/backend-api/models
req, _ := http.NewRequest("GET", "https://chat.openai.com/backend-api/models", nil)
// Add headers
req.Header.Add("Authorization", "Bearer "+auth.AuthResult.AccessToken)
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://chat.openai.com/")
req.Header.Add("Origin", "https://chat.openai.com")
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 {
return auth.AuthResult
}

View File

@ -2,16 +2,16 @@ module authenticator
go 1.20
require github.com/acheong08/OpenAIAuth v0.0.0-20230722134517-7f126fb9ee71
require (
github.com/bogdanfinn/fhttp v0.5.23
github.com/bogdanfinn/tls-client v1.5.0
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20220510032225-4f9f17eaec4c
)
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/bogdanfinn/fhttp v0.5.23 // indirect
github.com/bogdanfinn/tls-client v1.5.0 // indirect
github.com/bogdanfinn/utls v1.5.16 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20220510032225-4f9f17eaec4c // indirect
github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/net v0.12.0 // indirect

View File

@ -1,44 +1,22 @@
github.com/acheong08/OpenAIAuth v0.0.0-20230716132312-c206753c0819 h1:LE8tbbgfCcsUJZfQlpCF95eA+b2M6Q30ipzFjcpWp/I=
github.com/acheong08/OpenAIAuth v0.0.0-20230716132312-c206753c0819/go.mod h1:ES3Dh9hnbR2mDPlNTagj5e3b4nXECd4tbAjVgxggXEE=
github.com/acheong08/OpenAIAuth v0.0.0-20230716134840-dbf5ba4f9507 h1:1kZEmE1DeQEeIMHhQUqn+NqOdApF9BIv835ox09E2k8=
github.com/acheong08/OpenAIAuth v0.0.0-20230716134840-dbf5ba4f9507/go.mod h1:bkiXtklBFVpWHyWTys6Zhqb521i/gtT8cIUKWVx2m/M=
github.com/acheong08/OpenAIAuth v0.0.0-20230722134517-7f126fb9ee71 h1:IXULXNvaqA3gI6MDGAz/nW5snTC0iOqpm7pELmcyWyc=
github.com/acheong08/OpenAIAuth v0.0.0-20230722134517-7f126fb9ee71/go.mod h1:bkiXtklBFVpWHyWTys6Zhqb521i/gtT8cIUKWVx2m/M=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/bogdanfinn/fhttp v0.5.22 h1:U1jhZRtuaOanWWcm1WdMFnwMvSxUQgvO6berqAVTc5o=
github.com/bogdanfinn/fhttp v0.5.22/go.mod h1:brqi5woc5eSCVHdKYBV8aZLbO7HGqpwyDLeXW+fT18I=
github.com/bogdanfinn/fhttp v0.5.23 h1:4Xb5OjYArB8GpnUw4A4r5jmt8UW0/Cvey3R9nS2dC9U=
github.com/bogdanfinn/fhttp v0.5.23/go.mod h1:brqi5woc5eSCVHdKYBV8aZLbO7HGqpwyDLeXW+fT18I=
github.com/bogdanfinn/tls-client v1.3.12 h1:jpNj7owMY/oULUQyAhAv6tRFkliFGLyr8Qx1ZZY/gp8=
github.com/bogdanfinn/tls-client v1.3.12/go.mod h1:Q46nwIm0wPCweDM3XZcupxEIsTOWo3HVYSSsDj02/Qo=
github.com/bogdanfinn/tls-client v1.5.0 h1:L/d4AL+8RRw+6o9wwhpii45I6RO2CEkgRV4NeE0NAxo=
github.com/bogdanfinn/tls-client v1.5.0/go.mod h1:lgtqsHjoJYQMPz6H08bc8t30bmUaYnVjwtfVEzMGJDs=
github.com/bogdanfinn/utls v1.5.16 h1:NhhWkegEcYETBMj9nvgO4lwvc6NcLH+znrXzO3gnw4M=
github.com/bogdanfinn/utls v1.5.16/go.mod h1:mHeRCi69cUiEyVBkKONB1cAbLjRcZnlJbGzttmiuK4o=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20220510032225-4f9f17eaec4c h1:4RYnE0ISVwRxm9Dfo7utw1dh0kdRDEmVYq2MFVLy5zI=
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20220510032225-4f9f17eaec4c/go.mod h1:DvuJJ/w1Y59rG8UTDxsMk5U+UJXJwuvUgbiJSm9yhX8=
github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 h1:YqAladjX7xpA6BM04leXMWAEjS0mTZ5kUU9KRBriQJc=
github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5/go.mod h1:2JjD2zLQYH5HO74y5+aE3remJQvl6q4Sn6aWA2wD1Ng=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=

View File

@ -8,7 +8,7 @@ import (
"strings"
"time"
auth "github.com/acheong08/OpenAIAuth/auth"
auth "authenticator/auth"
)
type Account struct {

34
tools/authenticator/tojson.sh Executable file
View File

@ -0,0 +1,34 @@
#!/bin/bash
# Check if a file name is provided as an argument
if [ $# -eq 0 ]; then
echo "Usage: $0 <file>"
exit 1
fi
file="$1"
output="$2"
# Declare an empty array
lines=()
# Read the file line by line and add each line to the array
while IFS= read -r line; do
lines+=("\"$line\"")
done < "$file"
# Join array elements with commas and print the result enclosed in square brackets
result="["
for ((i = 0; i < ${#lines[@]}; i++)); do
result+="${lines[i]}"
if ((i < ${#lines[@]} - 1)); then
result+=","
fi
done
result+="]"
if [ $# -eq 1 ]; then
echo "$result"
fi
if [ $# -eq 2 ]; then
echo "$result" | tee $output
fi