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}