Skip to content

Commit 7b08bf9

Browse files
committed
fix(oauth): use wreq API for token requests
1 parent fe74754 commit 7b08bf9

1 file changed

Lines changed: 31 additions & 62 deletions

File tree

src/oauth.rs

Lines changed: 31 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use crate::{
55
oauth_resources::ANDROID_APP_VERSION_LIST,
66
};
77
use base64::{engine::general_purpose, Engine as _};
8-
use hyper::{client, Body, Method, Request};
98
use log::{error, info, trace, warn};
109
use serde_json::json;
1110
use tegen::tegen::TextGenerator;
@@ -88,7 +87,7 @@ impl Oauth {
8887
error!(
8988
"[⛔] Failed to create OAuth client: {}. Retrying in 5 seconds...",
9089
match e {
91-
AuthError::Hyper(error) => error.to_string(),
90+
AuthError::Wreq(error) => error.to_string(),
9291
AuthError::SerdeDeserialize(error) => error.to_string(),
9392
AuthError::Field((value, error)) => format!("{error}\n{value}"),
9493
}
@@ -142,14 +141,14 @@ impl Oauth {
142141

143142
#[derive(Debug)]
144143
enum AuthError {
145-
Hyper(hyper::Error),
144+
Wreq(wreq::Error),
146145
SerdeDeserialize(serde_json::Error),
147146
Field((serde_json::Value, &'static str)),
148147
}
149148

150-
impl From<hyper::Error> for AuthError {
151-
fn from(err: hyper::Error) -> Self {
152-
AuthError::Hyper(err)
149+
impl From<wreq::Error> for AuthError {
150+
fn from(err: wreq::Error) -> Self {
151+
AuthError::Wreq(err)
153152
}
154153
}
155154

@@ -222,42 +221,26 @@ impl OauthBackend for MobileSpoofAuth {
222221
async fn authenticate(&mut self) -> Result<OauthResponse, AuthError> {
223222
// Construct URL for OAuth token
224223
let url = format!("{AUTH_ENDPOINT}/auth/v2/oauth/access-token/loid");
225-
let mut builder = Request::builder().method(Method::POST).uri(&url);
226224

227-
// Add headers from spoofed client
225+
// Set up HTTP Basic Auth
226+
let auth = general_purpose::STANDARD.encode(format!("{}:", self.device.oauth_id));
227+
228+
// Build request with spoofed device headers
229+
let mut req_builder = CLIENT.post(&url).header("Authorization", format!("Basic {auth}"));
228230
for (key, value) in &self.device.initial_headers {
229-
builder = builder.header(key, value);
231+
req_builder = req_builder.header(key.as_str(), value.as_str());
230232
}
231-
// Set up HTTP Basic Auth - basically just the const OAuth ID's with no password,
232-
// Base64-encoded. https://en.wikipedia.org/wiki/Basic_access_authentication
233-
// This could be constant, but I don't think it's worth it. OAuth ID's can change
234-
// over time and we want to be flexible.
235-
let auth = general_purpose::STANDARD.encode(format!("{}:", self.device.oauth_id));
236-
builder = builder.header("Authorization", format!("Basic {auth}"));
237233

238-
// Set JSON body. I couldn't tell you what this means. But that's what the client sends
239-
let json = json!({
240-
"scopes": ["*","email", "pii"]
234+
// Set JSON body
235+
let json_body = json!({
236+
"scopes": ["*", "email", "pii"]
241237
});
242-
let body = Body::from(json.to_string());
243-
244-
// Build request
245-
let request = builder.body(body).unwrap();
246-
247-
trace!("Sending token request...\n\n{request:?}");
248-
249-
// Send request
250-
let client: &std::sync::LazyLock<client::Client<_, Body>> = &CLIENT;
251-
let resp = client.request(request).await?;
238+
let resp = req_builder.json(&json_body).send().await?;
252239

253240
trace!("Received response with status {} and length {:?}", resp.status(), resp.headers().get("content-length"));
254241
trace!("OAuth headers: {:#?}", resp.headers());
255242

256243
// Parse headers - loid header _should_ be saved sent on subsequent token refreshes.
257-
// Technically it's not needed, but it's easy for Reddit API to check for this.
258-
// It's some kind of header that uniquely identifies the device.
259-
// Not worried about the privacy implications, since this is randomly changed
260-
// and really only as privacy-concerning as the OAuth token itself.
261244
if let Some(header) = resp.headers().get("x-reddit-loid") {
262245
self.additional_headers.insert("x-reddit-loid".to_owned(), header.to_str().unwrap().to_string());
263246
}
@@ -270,8 +253,7 @@ impl OauthBackend for MobileSpoofAuth {
270253
trace!("Serializing response...");
271254

272255
// Serialize response
273-
let body_bytes = hyper::body::to_bytes(resp.into_body()).await?;
274-
let json: serde_json::Value = serde_json::from_slice(&body_bytes).map_err(AuthError::SerdeDeserialize)?;
256+
let json: serde_json::Value = resp.json().await.map_err(|e| AuthError::Wreq(e))?;
275257

276258
trace!("Accessing relevant fields...");
277259

@@ -341,40 +323,28 @@ impl OauthBackend for GenericWebAuth {
341323
async fn authenticate(&mut self) -> Result<OauthResponse, AuthError> {
342324
// Construct URL for OAuth token
343325
let url = "https://www.reddit.com/api/v1/access_token";
344-
let mut builder = Request::builder().method(Method::POST).uri(url);
345-
346-
// Add minimal headers
347-
builder = builder.header("Host", "www.reddit.com");
348-
builder = builder.header("User-Agent", &self.user_agent);
349-
builder = builder.header("Accept", "*/*");
350-
builder = builder.header("Accept-Language", "en-US,en;q=0.5");
351-
// builder = builder.header("Accept-Encoding", "gzip, deflate, br, zstd");
352-
builder = builder.header("Authorization", "Basic M1hmQkpXbGlIdnFBQ25YcmZJWWxMdzo=");
353-
builder = builder.header("Content-Type", "application/x-www-form-urlencoded");
354-
builder = builder.header("Sec-GPC", "1");
355-
builder = builder.header("Connection", "keep-alive");
356326

357327
// Set up form body
358328
let body_str = format!("grant_type=https%3A%2F%2Foauth.reddit.com%2Fgrants%2Finstalled_client&device_id={}", self.device_id);
359-
let body = Body::from(body_str);
360-
361-
// Build request
362-
let request = builder.body(body).unwrap();
363329

364-
trace!("Sending GenericWebAuth token request...\n\n{request:?}");
365-
366-
// Send request
367-
let client: &std::sync::LazyLock<client::Client<_, Body>> = &CLIENT;
368-
let resp = client.request(request).await?;
330+
let resp = CLIENT
331+
.post(url)
332+
.header("Host", "www.reddit.com")
333+
.header("User-Agent", &self.user_agent)
334+
.header("Accept", "*/*")
335+
.header("Accept-Language", "en-US,en;q=0.5")
336+
.header("Authorization", "Basic M1hmQkpXbGlIdnFBQ25YcmZJWWxMdzo=")
337+
.header("Content-Type", "application/x-www-form-urlencoded")
338+
.header("Sec-GPC", "1")
339+
.header("Connection", "keep-alive")
340+
.body(body_str)
341+
.send()
342+
.await?;
369343

370344
trace!("Received response with status {} and length {:?}", resp.status(), resp.headers().get("content-length"));
371345
trace!("GenericWebAuth headers: {:#?}", resp.headers());
372346

373-
// Parse headers - loid header _should_ be saved sent on subsequent token refreshes.
374-
// Technically it's not needed, but it's easy for Reddit API to check for this.
375-
// It's some kind of header that uniquely identifies the device.
376-
// Not worried about the privacy implications, since this is randomly changed
377-
// and really only as privacy-concerning as the OAuth token itself.
347+
// Parse headers
378348
if let Some(header) = resp.headers().get("x-reddit-loid") {
379349
self.additional_headers.insert("x-reddit-loid".to_owned(), header.to_str().unwrap().to_string());
380350
}
@@ -387,8 +357,7 @@ impl OauthBackend for GenericWebAuth {
387357
trace!("Serializing GenericWebAuth response...");
388358

389359
// Serialize response
390-
let body_bytes = hyper::body::to_bytes(resp.into_body()).await?;
391-
let json: serde_json::Value = serde_json::from_slice(&body_bytes).map_err(AuthError::SerdeDeserialize)?;
360+
let json: serde_json::Value = resp.json().await.map_err(|e| AuthError::Wreq(e))?;
392361

393362
trace!("Accessing relevant fields...");
394363

0 commit comments

Comments
 (0)