chatgpt/
auth.rs

1use std::string::ToString;
2use std::sync::Arc;
3
4use regex::Regex;
5use reqwest::cookie::{self};
6use reqwest::header::{self, HeaderMap, HeaderValue};
7use reqwest::Proxy;
8
9pub struct Authenticator {
10    pub session_token: Option<String>,
11    email_address: String,
12    password: String,
13    session: reqwest::Client,
14    pub access_token: Option<String>,
15    user_agent: String,
16    cookie_store: Arc<cookie::Jar>,
17}
18
19impl Authenticator {
20    pub fn new(email_address: Option<&str>, password: Option<&str>, proxy: Option<Proxy>) -> Self {
21        let cookie_store = Arc::new(cookie::Jar::default());
22        let mut client = reqwest::Client::builder()
23            .cookie_store(true)
24            .cookie_provider(cookie_store.clone());
25        if let Some(proxy) = proxy {
26            client = client.proxy(proxy);
27        }
28        Self {
29            session_token: None,
30            email_address: email_address.unwrap_or("").to_string(),
31            password: password.unwrap_or("").to_string(),
32            session: client.build().unwrap(),
33            access_token: None,
34            user_agent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 \
35                         Safari/537.36"
36                .to_string(),
37            cookie_store,
38        }
39    }
40
41    fn url_encode(string: &str) -> String {
42        urlencoding::encode(string).to_string()
43    }
44
45    pub async fn begin(&mut self) {
46        let url = "https://explorer.api.openai.com/api/auth/csrf";
47        let headers: HeaderMap<HeaderValue> = [
48            (header::HOST, "explorer.api.openai.com".parse().unwrap()),
49            (header::ACCEPT, "*/*".parse().unwrap()),
50            (header::CONNECTION, "keep-alive".parse().unwrap()),
51            (header::USER_AGENT, self.user_agent.parse().unwrap()),
52            (header::ACCEPT_LANGUAGE, "en-GB,en-US;q=0.9,en;q=0.8".parse().unwrap()),
53            (
54                header::REFERER,
55                "https://explorer.api.openai.com/auth/login".parse().unwrap(),
56            ),
57            (header::ACCEPT_ENCODING, "gzip, deflate, br".parse().unwrap()),
58        ]
59        .into_iter()
60        .collect();
61
62        let response = self.session.get(url).headers(headers).send().await.unwrap();
63        if response.status().is_success()
64            && response
65                .headers()
66                .get("Content-Type")
67                .unwrap()
68                .to_str()
69                .unwrap()
70                .contains("json")
71        {
72            let csrf_token = response.json::<serde_json::Value>().await.unwrap()["csrfToken"]
73                .as_str()
74                .unwrap()
75                .to_string();
76            self.__part_one(&csrf_token).await;
77        }
78        else {
79            panic!("Error: {}", response.text().await.unwrap());
80        }
81    }
82
83    async fn __part_one(&mut self, token: &str) {
84        let url = "https://explorer.api.openai.com/api/auth/signin/auth0?prompt=login";
85        let payload = format!("callbackUrl=%2F&csrfToken={token}&json=true");
86        let headers: HeaderMap<HeaderValue> = [
87            (header::HOST, "explorer.api.openai.com".parse().unwrap()),
88            (header::USER_AGENT, self.user_agent.parse().unwrap()),
89            (
90                header::CONTENT_TYPE,
91                "application/x-www-form-urlencoded".parse().unwrap(),
92            ),
93            (header::ACCEPT, "*/*".parse().unwrap()),
94            (header::HeaderName::from_static("Sec-Gpc"), "1".parse().unwrap()),
95            (header::ACCEPT_LANGUAGE, "en-US,en;q=0.8".parse().unwrap()),
96            (header::ORIGIN, "https://explorer.api.openai.com".parse().unwrap()),
97            (
98                header::HeaderName::from_static("Sec-Fetch-Site"),
99                "same-origin".parse().unwrap(),
100            ),
101            (
102                header::HeaderName::from_static("Sec-Fetch-Mode"),
103                "cors".parse().unwrap(),
104            ),
105            (
106                header::HeaderName::from_static("Sec-Fetch-Dest"),
107                "empty".parse().unwrap(),
108            ),
109            (
110                header::REFERER,
111                "https://explorer.api.openai.com/auth/login".parse().unwrap(),
112            ),
113            (header::ACCEPT_ENCODING, "gzip, deflate".parse().unwrap()),
114        ]
115        .into_iter()
116        .collect();
117
118        let response = self
119            .session
120            .post(url)
121            .headers(headers)
122            .body(payload)
123            .send()
124            .await
125            .unwrap();
126        if response.status().is_success()
127            && response
128                .headers()
129                .get("Content-Type")
130                .unwrap()
131                .to_str()
132                .unwrap()
133                .contains("json")
134        {
135            let url = response.json::<serde_json::Value>().await.unwrap()["url"]
136                .as_str()
137                .unwrap()
138                .to_string();
139            assert!(
140                !(url == "https://explorer.api.openai.com/api/auth/error?error=OAuthSignin" || url.contains("error")),
141                "You have been rate limited. Please try again later."
142            );
143            self.__part_two(&url).await;
144        }
145        else {
146            panic!("Error: {}", response.text().await.unwrap());
147        }
148    }
149
150    async fn __part_two(&mut self, url: &str) {
151        let headers: HeaderMap<HeaderValue> = [
152            (header::HOST, "auth0.openai.com".parse().unwrap()),
153            (
154                header::ACCEPT,
155                "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
156                    .parse()
157                    .unwrap(),
158            ),
159            (header::CONNECTION, "keep-alive".parse().unwrap()),
160            (header::USER_AGENT, self.user_agent.parse().unwrap()),
161            (header::ACCEPT_LANGUAGE, "en-US,en;q=0.9".parse().unwrap()),
162            (header::REFERER, "https://explorer.api.openai.com/".parse().unwrap()),
163        ]
164        .into_iter()
165        .collect();
166
167        let response = self.session.get(url).headers(headers).send().await.unwrap();
168        if response.status().is_redirection() || response.status().is_success() {
169            let state = Regex::new(r"state=(.*)")
170                .unwrap()
171                .captures(&response.text().await.unwrap())
172                .unwrap()[1]
173                .to_string();
174            self.__part_three(&state).await;
175        }
176        else {
177            panic!("Error: {}", response.text().await.unwrap());
178        }
179    }
180
181    async fn __part_three(&mut self, state: &str) {
182        let url = format!("https://auth0.openai.com/u/login/identifier?state={state}");
183
184        let headers: HeaderMap<HeaderValue> = [
185            (header::HOST, "auth0.openai.com".parse().unwrap()),
186            (
187                header::ACCEPT,
188                "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
189                    .parse()
190                    .unwrap(),
191            ),
192            (header::CONNECTION, "keep-alive".parse().unwrap()),
193            (header::USER_AGENT, self.user_agent.parse().unwrap()),
194            (header::ACCEPT_LANGUAGE, "en-US,en;q=0.9".parse().unwrap()),
195            (header::REFERER, "https://explorer.api.openai.com/".parse().unwrap()),
196        ]
197        .into_iter()
198        .collect();
199
200        let response = self.session.get(url).headers(headers).send().await.unwrap();
201        if response.status().is_success() {
202            self.__part_four(state).await;
203        }
204        else {
205            panic!("Error: {}", response.text().await.unwrap());
206        }
207    }
208
209    async fn __part_four(&mut self, state: &str) {
210        let url = format!("https://auth0.openai.com/u/login/identifier?state={state}");
211        let email_url_encoded = Self::url_encode(&self.email_address);
212
213        let payload = format!(
214            "state={state}&username={email_url_encoded}&js-available=false&webauthn-available=true&is-brave=false&\
215             webauthn-platform-available=true&action=default"
216        );
217
218        let headers: HeaderMap<HeaderValue> = [
219            (header::HOST, "auth0.openai.com".parse().unwrap()),
220            (header::ORIGIN, "https://auth0.openai.com".parse().unwrap()),
221            (header::CONNECTION, "keep-alive".parse().unwrap()),
222            (
223                header::ACCEPT,
224                "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
225                    .parse()
226                    .unwrap(),
227            ),
228            (header::USER_AGENT, self.user_agent.parse().unwrap()),
229            (
230                header::REFERER,
231                format!("https://auth0.openai.com/u/login/identifier?state={state}")
232                    .parse()
233                    .unwrap(),
234            ),
235            (header::ACCEPT_LANGUAGE, "en-US,en;q=0.9".parse().unwrap()),
236            (
237                header::CONTENT_TYPE,
238                "application/x-www-form-urlencoded".parse().unwrap(),
239            ),
240        ]
241        .into_iter()
242        .collect();
243
244        let response = self
245            .session
246            .post(url)
247            .headers(headers)
248            .body(payload)
249            .send()
250            .await
251            .unwrap();
252        if response.status().is_redirection() || response.status().is_success() {
253            self.__part_five(state).await;
254        }
255        else {
256            panic!("Error: {}", response.text().await.unwrap());
257        }
258    }
259
260    async fn __part_five(&mut self, state: &str) {
261        let url = format!("https://auth0.openai.com/u/login/password?state={state}");
262        let email_url_encoded = Self::url_encode(&self.email_address);
263        let password_url_encoded = Self::url_encode(&self.password);
264        let payload =
265            format!("state={state}&username={email_url_encoded}&password={password_url_encoded}&action=default");
266
267        let headers: HeaderMap<HeaderValue> = [
268            (header::HOST, "auth0.openai.com".parse().unwrap()),
269            (header::ORIGIN, "https://auth0.openai.com".parse().unwrap()),
270            (header::CONNECTION, "keep-alive".parse().unwrap()),
271            (
272                header::ACCEPT,
273                "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
274                    .parse()
275                    .unwrap(),
276            ),
277            (header::USER_AGENT, self.user_agent.parse().unwrap()),
278            (
279                header::REFERER,
280                format!("https://auth0.openai.com/u/login/password?state={state}")
281                    .parse()
282                    .unwrap(),
283            ),
284            (header::ACCEPT_LANGUAGE, "en-US,en;q=0.9".parse().unwrap()),
285            (
286                header::CONTENT_TYPE,
287                "application/x-www-form-urlencoded".parse().unwrap(),
288            ),
289        ]
290        .into_iter()
291        .collect();
292
293        let response = self
294            .session
295            .post(url)
296            .headers(headers)
297            .body(payload)
298            .send()
299            .await
300            .unwrap();
301        if response.status().is_redirection() || response.status().is_success() {
302            let text = response.text().await.unwrap();
303            let captures = Regex::new(r"state=(.*)").unwrap().captures(&text).unwrap();
304            let new_state = captures[1].split('"').next().unwrap();
305            self.__part_six(state, new_state).await;
306        }
307        else {
308            panic!("Error: {}", response.text().await.unwrap());
309        }
310    }
311
312    async fn __part_six(&mut self, old_state: &str, new_state: &str) {
313        let url = format!("https://auth0.openai.com/authorize/resume?state={new_state}");
314        let headers: HeaderMap<HeaderValue> = [
315            (header::HOST, "auth0.openai.com".parse().unwrap()),
316            (
317                header::ACCEPT,
318                "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
319                    .parse()
320                    .unwrap(),
321            ),
322            (header::CONNECTION, "keep-alive".parse().unwrap()),
323            (header::USER_AGENT, self.user_agent.parse().unwrap()),
324            (header::ACCEPT_LANGUAGE, "en-GB,en-US;q=0.9,en;q=0.8".parse().unwrap()),
325            (
326                header::REFERER,
327                format!("https://auth0.openai.com/u/login/password?state={old_state}")
328                    .parse()
329                    .unwrap(),
330            ),
331        ]
332        .into_iter()
333        .collect();
334
335        let response = self.session.get(&url).headers(headers).send().await.unwrap();
336        if response.status().is_redirection() {
337            let redirect_url = response.headers().get("location").unwrap();
338            self.__part_seven(redirect_url.to_str().unwrap(), &url).await;
339        }
340        else {
341            panic!("Error: {}", response.text().await.unwrap());
342        }
343    }
344
345    async fn __part_seven(&mut self, redirect_url: &str, previous_url: &str) {
346        let url = redirect_url;
347        let headers: HeaderMap<HeaderValue> = [
348            (header::HOST, "explorer.api.openai.com".parse().unwrap()),
349            (header::ACCEPT, "application/json".parse().unwrap()),
350            (header::CONNECTION, "keep-alive".parse().unwrap()),
351            (header::USER_AGENT, self.user_agent.parse().unwrap()),
352            (header::ACCEPT_LANGUAGE, "en-GB,en-US;q=0.9,en;q=0.8".parse().unwrap()),
353            (header::REFERER, previous_url.parse().unwrap()),
354        ]
355        .into_iter()
356        .collect();
357
358        let response = self.session.get(url).headers(headers).send().await.unwrap();
359        if response.status().is_redirection() {
360            self.session_token = Some(
361                response
362                    .cookies()
363                    .find(|cookie| cookie.name() == "__Secure-next-auth.session-token")
364                    .unwrap()
365                    .value()
366                    .to_string(),
367            );
368            self.get_access_token().await;
369        }
370        else {
371            panic!("Error: {}", response.text().await.unwrap());
372        }
373    }
374
375    pub async fn get_access_token(&mut self) {
376        let url = "https://explorer.api.openai.com/api/auth/session".parse().unwrap();
377        let cookie = format!(
378            "{}={}",
379            "__Secure-next-auth.session-token",
380            self.session_token.as_ref().unwrap()
381        );
382        self.cookie_store.add_cookie_str(&cookie, &url);
383        let response = self.session.get(url).send().await.unwrap();
384        if response.status().is_success() {
385            self.access_token = Some(
386                response.json::<serde_json::Value>().await.unwrap()["accessToken"]
387                    .as_str()
388                    .unwrap()
389                    .to_string(),
390            );
391        }
392        else {
393            panic!("Error: {}", response.text().await.unwrap());
394        }
395    }
396}