use reqwest::header::{
AUTHORIZATION, CONTENT_TYPE, COOKIE, HeaderMap, HeaderName, HeaderValue, LOCATION, USER_AGENT,
};
use serde::Serialize;
use crate::{PSNApiResult, PSNClient, models::TokenResponse};
impl PSNClient {
pub async fn authenticate(&mut self) -> PSNApiResult<()> {
let authorization_code = self.get_authorization_code().await?;
log::debug!("got authorization code");
self.fetch_access_code_from_authorization(authorization_code)
.await?;
log::debug!("successfully authenticated");
Ok(())
}
async fn get_authorization_code(&mut self) -> PSNApiResult<String> {
log::debug!("getting authorization code");
let mut headers = HeaderMap::new();
headers.insert(
COOKIE,
HeaderValue::from_str(&format!("npsso={}", self.npsso))?,
);
headers.insert(
CONTENT_TYPE,
HeaderValue::from_static("application/x-www-form-urlencoded"),
);
headers.insert(
"X-Requested-With",
HeaderValue::from_static("com.scee.psxandroid"),
);
headers.insert("Sec-Fetch-Dest", HeaderValue::from_static("document"));
headers.insert("Sec-Fetch-Mode", HeaderValue::from_static("navigate"));
headers.insert("Sec-Fetch-Site", HeaderValue::from_static("same-site"));
headers.insert("Sec-Fetch-User", HeaderValue::from_static("?1"));
let query = vec![
("access_type", "offline"),
("cid", self.cid.as_str()),
("client_id", "09515159-7237-4370-9b40-3806e67c0891"),
("device_base_font_size", "10"),
("device_profile", "mobile"),
("elements_visibility", "no_aclink"),
("enable_scheme_error_code", "true"),
("no_captcha", "true"),
("PlatformPrivacyWs1", "minimal"),
("redirect_uri", "com.scee.psxandroid.scecompcall://redirect"),
("response_type", "code"),
("scope", "psn:mobile.v2.core psn:clientapp"),
("service_entity", "urn:service-entity:psn"),
("service_logo", "ps"),
("smcid", "psapp:signin"),
("support_scheme", "sneiprls"),
("turnOnTrustedBrowser", "true"),
("ui", "pr"),
];
let response = self
.get(
"https://ca.account.sony.com/api/authz/v3/oauth/authorize",
Some(headers),
Some(query),
)
.await?;
let location_url = response.headers().get(LOCATION).unwrap().to_str().unwrap();
let parsed_location_url = url::Url::parse(location_url)?;
let parsed_location_query_pairs = parsed_location_url.query_pairs().collect::<Vec<_>>();
let filtered = parsed_location_query_pairs
.iter()
.filter(|v| v.0 == "code")
.collect::<Vec<_>>();
let authorization_code = filtered.first().unwrap().1.to_string();
Ok(authorization_code)
}
async fn fetch_access_code_from_authorization<S: AsRef<str>>(
&mut self,
authorization_code: S,
) -> PSNApiResult<()> {
log::debug!("fetching access code from authorization");
let mut headers = HeaderMap::new();
headers.insert(
AUTHORIZATION,
HeaderValue::from_static(
"Basic MDk1MTUxNTktNzIzNy00MzcwLTliNDAtMzgwNmU2N2MwODkxOnVjUGprYTV0bnRCMktxc1A=",
),
);
headers.insert(
CONTENT_TYPE,
HeaderValue::from_static("application/x-www-form-urlencoded"),
);
headers.insert(
USER_AGENT,
HeaderValue::from_static("com.sony.snei.np.android.sso.share.oauth.versa.USER_AGENT"),
);
headers.insert(
HeaderName::from_static("x-psn-correlation-id"),
HeaderValue::from_str(self.cid.as_str())?,
);
#[derive(Serialize)]
struct Data {
cid: String,
code: String,
grant_type: String,
redirect_uri: String,
scope: String,
token_format: String,
}
let data = Data {
cid: self.cid.clone(),
code: authorization_code.as_ref().to_string(),
grant_type: "authorization_code".to_string(),
redirect_uri: "com.scee.psxandroid.scecompcall://redirect".to_string(),
scope: "psn:mobile.v2.core psn:clientapp".to_string(),
token_format: "jwt".to_string(),
};
let response = self
.post(
"https://ca.account.sony.com/api/authz/v3/oauth/token",
Some(headers),
Some(&data),
)
.await?
.error_for_status()?;
let token_response = response.json::<TokenResponse>().await?;
self.token_response = Some(token_response);
Ok(())
}
}