You've already forked AstralRinth
forked from didirus/AstralRinth
Fix auth timestamps (#1184)
* Fix auth timestamps * Update error message, get valid timestamp on token refresh * fix lint
This commit is contained in:
@@ -106,7 +106,8 @@ pub enum ErrorKind {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
source: tracing_error::TracedError<ErrorKind>,
|
||||
pub raw: std::sync::Arc<ErrorKind>,
|
||||
pub source: tracing_error::TracedError<std::sync::Arc<ErrorKind>>,
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
@@ -123,8 +124,12 @@ impl std::fmt::Display for Error {
|
||||
|
||||
impl<E: Into<ErrorKind>> From<E> for Error {
|
||||
fn from(source: E) -> Self {
|
||||
let error = Into::<ErrorKind>::into(source);
|
||||
let boxed_error = std::sync::Arc::new(error);
|
||||
|
||||
Self {
|
||||
source: Into::<ErrorKind>::into(source).in_current_span(),
|
||||
raw: boxed_error.clone(),
|
||||
source: boxed_error.in_current_span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use crate::data::DirectoryInfo;
|
||||
use crate::util::fetch::{read_json, write, IoSemaphore, REQWEST_CLIENT};
|
||||
use crate::State;
|
||||
use crate::{ErrorKind, State};
|
||||
use base64::prelude::{BASE64_STANDARD, BASE64_URL_SAFE_NO_PAD};
|
||||
use base64::Engine;
|
||||
use byteorder::BigEndian;
|
||||
use chrono::{DateTime, Duration, NaiveDate, Utc};
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use p256::ecdsa::signature::Signer;
|
||||
use p256::ecdsa::{Signature, SigningKey, VerifyingKey};
|
||||
use p256::pkcs8::{DecodePrivateKey, EncodePrivateKey, LineEnding};
|
||||
use rand::rngs::OsRng;
|
||||
use rand::Rng;
|
||||
use reqwest::header::HeaderMap;
|
||||
use reqwest::{Error, Response};
|
||||
use reqwest::Response;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
@@ -82,8 +82,6 @@ pub struct SaveDeviceToken {
|
||||
pub x: String,
|
||||
pub y: String,
|
||||
pub token: DeviceToken,
|
||||
#[serde(default)]
|
||||
modern: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@@ -136,11 +134,13 @@ impl MinecraftAuthStore {
|
||||
#[tracing::instrument(skip(self))]
|
||||
async fn refresh_and_get_device_token(
|
||||
&mut self,
|
||||
) -> crate::Result<(DeviceTokenKey, DeviceToken)> {
|
||||
current_date: DateTime<Utc>,
|
||||
force_generate: bool,
|
||||
) -> crate::Result<(DeviceTokenKey, DeviceToken, DateTime<Utc>, bool)> {
|
||||
macro_rules! generate_key {
|
||||
($self:ident, $generate_key:expr, $device_token:expr, $SaveDeviceToken:path) => {{
|
||||
let key = generate_key()?;
|
||||
let token = device_token(&key).await?;
|
||||
let res = device_token(&key, current_date).await?;
|
||||
|
||||
self.token = Some(SaveDeviceToken {
|
||||
id: key.id.clone(),
|
||||
@@ -153,30 +153,20 @@ impl MinecraftAuthStore {
|
||||
.to_string(),
|
||||
x: key.x.clone(),
|
||||
y: key.y.clone(),
|
||||
token: token.clone(),
|
||||
modern: true,
|
||||
token: res.value.clone(),
|
||||
});
|
||||
self.save().await?;
|
||||
|
||||
(key, token)
|
||||
(key, res.value, res.date, true)
|
||||
}};
|
||||
}
|
||||
|
||||
let (key, token) = if let Some(ref token) = self.token {
|
||||
// reset device token for legacy launcher versions with broken values
|
||||
if self.users.is_empty() && !token.modern {
|
||||
return Ok(generate_key!(
|
||||
self,
|
||||
generate_key,
|
||||
device_token,
|
||||
SaveDeviceToken
|
||||
));
|
||||
}
|
||||
|
||||
if token.token.not_after > Utc::now() {
|
||||
if let Ok(private_key) =
|
||||
SigningKey::from_pkcs8_pem(&token.private_key)
|
||||
{
|
||||
let (key, token, date, valid_date) = if let Some(ref token) = self.token
|
||||
{
|
||||
if let Ok(private_key) =
|
||||
SigningKey::from_pkcs8_pem(&token.private_key)
|
||||
{
|
||||
if token.token.not_after > Utc::now() && !force_generate {
|
||||
(
|
||||
DeviceTokenKey {
|
||||
id: token.id.clone(),
|
||||
@@ -185,14 +175,20 @@ impl MinecraftAuthStore {
|
||||
y: token.y.clone(),
|
||||
},
|
||||
token.token.clone(),
|
||||
current_date,
|
||||
false,
|
||||
)
|
||||
} else {
|
||||
generate_key!(
|
||||
self,
|
||||
generate_key,
|
||||
device_token,
|
||||
SaveDeviceToken
|
||||
)
|
||||
let key = DeviceTokenKey {
|
||||
id: token.id.clone(),
|
||||
key: private_key,
|
||||
x: token.x.clone(),
|
||||
y: token.y.clone(),
|
||||
};
|
||||
|
||||
let res = device_token(&key, current_date).await?;
|
||||
|
||||
(key, res.value, res.date, true)
|
||||
}
|
||||
} else {
|
||||
generate_key!(self, generate_key, device_token, SaveDeviceToken)
|
||||
@@ -201,28 +197,60 @@ impl MinecraftAuthStore {
|
||||
generate_key!(self, generate_key, device_token, SaveDeviceToken)
|
||||
};
|
||||
|
||||
Ok((key, token))
|
||||
Ok((key, token, date, valid_date))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub async fn login_begin(&mut self) -> crate::Result<MinecraftLoginFlow> {
|
||||
let (key, token) = self.refresh_and_get_device_token().await?;
|
||||
let (key, token, current_date, valid_date) =
|
||||
self.refresh_and_get_device_token(Utc::now(), false).await?;
|
||||
|
||||
let verifier = generate_oauth_challenge();
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(&verifier);
|
||||
let result = hasher.finalize();
|
||||
let challenge = BASE64_URL_SAFE_NO_PAD.encode(&result);
|
||||
let challenge = BASE64_URL_SAFE_NO_PAD.encode(result);
|
||||
|
||||
let (session_id, redirect_uri) =
|
||||
sisu_authenticate(&token.token, &challenge, &key).await?;
|
||||
match sisu_authenticate(&token.token, &challenge, &key, current_date)
|
||||
.await
|
||||
{
|
||||
Ok((session_id, redirect_uri)) => Ok(MinecraftLoginFlow {
|
||||
verifier,
|
||||
challenge,
|
||||
session_id,
|
||||
redirect_uri: redirect_uri.value.msa_oauth_redirect,
|
||||
}),
|
||||
Err(err) => {
|
||||
if !valid_date {
|
||||
let (key, token, current_date, _) = self
|
||||
.refresh_and_get_device_token(Utc::now(), false)
|
||||
.await?;
|
||||
|
||||
Ok(MinecraftLoginFlow {
|
||||
verifier,
|
||||
challenge,
|
||||
session_id,
|
||||
redirect_uri: redirect_uri.msa_oauth_redirect,
|
||||
})
|
||||
let verifier = generate_oauth_challenge();
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(&verifier);
|
||||
let result = hasher.finalize();
|
||||
let challenge = BASE64_URL_SAFE_NO_PAD.encode(result);
|
||||
|
||||
let (session_id, redirect_uri) = sisu_authenticate(
|
||||
&token.token,
|
||||
&challenge,
|
||||
&key,
|
||||
current_date,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(MinecraftLoginFlow {
|
||||
verifier,
|
||||
challenge,
|
||||
session_id,
|
||||
redirect_uri: redirect_uri.value.msa_oauth_redirect,
|
||||
})
|
||||
} else {
|
||||
Err(crate::ErrorKind::from(err).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
@@ -231,20 +259,27 @@ impl MinecraftAuthStore {
|
||||
code: &str,
|
||||
flow: MinecraftLoginFlow,
|
||||
) -> crate::Result<Credentials> {
|
||||
let (key, token) = self.refresh_and_get_device_token().await?;
|
||||
let (key, token, _, _) =
|
||||
self.refresh_and_get_device_token(Utc::now(), false).await?;
|
||||
|
||||
let oauth_token = oauth_token(code, &flow.verifier).await?;
|
||||
let sisu_authorize = sisu_authorize(
|
||||
Some(&flow.session_id),
|
||||
&oauth_token.access_token,
|
||||
&oauth_token.value.access_token,
|
||||
&token.token,
|
||||
&key,
|
||||
oauth_token.date,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let xbox_token =
|
||||
xsts_authorize(sisu_authorize, &token.token, &key).await?;
|
||||
let minecraft_token = minecraft_token(xbox_token).await?;
|
||||
let xbox_token = xsts_authorize(
|
||||
sisu_authorize.value,
|
||||
&token.token,
|
||||
&key,
|
||||
sisu_authorize.date,
|
||||
)
|
||||
.await?;
|
||||
let minecraft_token = minecraft_token(xbox_token.value).await?;
|
||||
|
||||
minecraft_entitlements(&minecraft_token.access_token).await?;
|
||||
|
||||
@@ -256,9 +291,9 @@ impl MinecraftAuthStore {
|
||||
id: profile_id,
|
||||
username: profile.name,
|
||||
access_token: minecraft_token.access_token,
|
||||
refresh_token: oauth_token.refresh_token,
|
||||
expires: Utc::now()
|
||||
+ Duration::seconds(oauth_token.expires_in as i64),
|
||||
refresh_token: oauth_token.value.refresh_token,
|
||||
expires: oauth_token.date
|
||||
+ Duration::seconds(oauth_token.value.expires_in as i64),
|
||||
};
|
||||
|
||||
self.users.insert(profile_id, credentials.clone());
|
||||
@@ -272,6 +307,52 @@ impl MinecraftAuthStore {
|
||||
Ok(credentials)
|
||||
}
|
||||
|
||||
async fn refresh_token(
|
||||
&mut self,
|
||||
creds: &Credentials,
|
||||
) -> crate::Result<Option<Credentials>> {
|
||||
let cred_id = creds.id;
|
||||
let profile_name = creds.username.clone();
|
||||
|
||||
let oauth_token = oauth_refresh(&creds.refresh_token).await?;
|
||||
let (key, token, current_date, _) = self
|
||||
.refresh_and_get_device_token(oauth_token.date, false)
|
||||
.await?;
|
||||
|
||||
let sisu_authorize = sisu_authorize(
|
||||
None,
|
||||
&oauth_token.value.access_token,
|
||||
&token.token,
|
||||
&key,
|
||||
current_date,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let xbox_token = xsts_authorize(
|
||||
sisu_authorize.value,
|
||||
&token.token,
|
||||
&key,
|
||||
sisu_authorize.date,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let minecraft_token = minecraft_token(xbox_token.value).await?;
|
||||
|
||||
let val = Credentials {
|
||||
id: cred_id,
|
||||
username: profile_name,
|
||||
access_token: minecraft_token.access_token,
|
||||
refresh_token: oauth_token.value.refresh_token,
|
||||
expires: oauth_token.date
|
||||
+ Duration::seconds(oauth_token.value.expires_in as i64),
|
||||
};
|
||||
|
||||
self.users.insert(val.id, val.clone());
|
||||
self.save().await?;
|
||||
|
||||
Ok(Some(val))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub async fn get_default_credential(
|
||||
&mut self,
|
||||
@@ -293,38 +374,28 @@ impl MinecraftAuthStore {
|
||||
}
|
||||
|
||||
if creds.expires < Utc::now() {
|
||||
let cred_id = creds.id;
|
||||
let profile_name = creds.username.clone();
|
||||
let old_credentials = creds.clone();
|
||||
|
||||
let oauth_token = oauth_refresh(&creds.refresh_token).await?;
|
||||
let (key, token) = self.refresh_and_get_device_token().await?;
|
||||
let res = self.refresh_token(&old_credentials).await;
|
||||
|
||||
let sisu_authorize = sisu_authorize(
|
||||
None,
|
||||
&oauth_token.access_token,
|
||||
&token.token,
|
||||
&key,
|
||||
)
|
||||
.await?;
|
||||
match res {
|
||||
Ok(val) => Ok(val),
|
||||
Err(err) => {
|
||||
if let ErrorKind::MinecraftAuthenticationError(
|
||||
MinecraftAuthenticationError::Request {
|
||||
ref source,
|
||||
..
|
||||
},
|
||||
) = *err.raw
|
||||
{
|
||||
if source.is_connect() || source.is_timeout() {
|
||||
return Ok(Some(old_credentials));
|
||||
}
|
||||
}
|
||||
|
||||
let xbox_token =
|
||||
xsts_authorize(sisu_authorize, &token.token, &key).await?;
|
||||
|
||||
let minecraft_token = minecraft_token(xbox_token).await?;
|
||||
|
||||
let val = Credentials {
|
||||
id: cred_id,
|
||||
username: profile_name,
|
||||
access_token: minecraft_token.access_token,
|
||||
refresh_token: oauth_token.refresh_token,
|
||||
expires: Utc::now()
|
||||
+ Duration::seconds(oauth_token.expires_in as i64),
|
||||
};
|
||||
|
||||
self.users.insert(val.id, val.clone());
|
||||
self.save().await?;
|
||||
|
||||
Ok(Some(val))
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(Some(creds.clone()))
|
||||
}
|
||||
@@ -357,6 +428,11 @@ const MICROSOFT_CLIENT_ID: &str = "00000000402b5328";
|
||||
const REDIRECT_URL: &str = "https://login.live.com/oauth20_desktop.srf";
|
||||
const REQUESTED_SCOPES: &str = "service::user.auth.xboxlive.com::MBI_SSL";
|
||||
|
||||
struct RequestWithDate<T> {
|
||||
pub date: DateTime<Utc>,
|
||||
pub value: T,
|
||||
}
|
||||
|
||||
// flow steps
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
@@ -370,8 +446,9 @@ pub struct DeviceToken {
|
||||
#[tracing::instrument(skip(key))]
|
||||
pub async fn device_token(
|
||||
key: &DeviceTokenKey,
|
||||
) -> Result<DeviceToken, MinecraftAuthenticationError> {
|
||||
Ok(send_signed_request(
|
||||
current_date: DateTime<Utc>,
|
||||
) -> Result<RequestWithDate<DeviceToken>, MinecraftAuthenticationError> {
|
||||
let res = send_signed_request(
|
||||
None,
|
||||
"https://device.auth.xboxlive.com/device/authenticate",
|
||||
"/device/authenticate",
|
||||
@@ -396,9 +473,14 @@ pub async fn device_token(
|
||||
}),
|
||||
key,
|
||||
MinecraftAuthStep::GetDeviceToken,
|
||||
current_date,
|
||||
)
|
||||
.await?
|
||||
.1)
|
||||
.await?;
|
||||
|
||||
Ok(RequestWithDate {
|
||||
date: res.current_date,
|
||||
value: res.body,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -412,8 +494,10 @@ async fn sisu_authenticate(
|
||||
token: &str,
|
||||
challenge: &str,
|
||||
key: &DeviceTokenKey,
|
||||
) -> Result<(String, RedirectUri), MinecraftAuthenticationError> {
|
||||
let (headers, res) = send_signed_request(
|
||||
current_date: DateTime<Utc>,
|
||||
) -> Result<(String, RequestWithDate<RedirectUri>), MinecraftAuthenticationError>
|
||||
{
|
||||
let res = send_signed_request::<RedirectUri>(
|
||||
None,
|
||||
"https://sisu.xboxlive.com/authenticate",
|
||||
"/authenticate",
|
||||
@@ -436,16 +520,24 @@ async fn sisu_authenticate(
|
||||
}),
|
||||
key,
|
||||
MinecraftAuthStep::SisuAuthenicate,
|
||||
current_date,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let session_id = headers
|
||||
let session_id = res
|
||||
.headers
|
||||
.get("X-SessionId")
|
||||
.and_then(|x| x.to_str().ok())
|
||||
.ok_or_else(|| MinecraftAuthenticationError::NoSessionId)?
|
||||
.to_string();
|
||||
|
||||
Ok((session_id, res))
|
||||
Ok((
|
||||
session_id,
|
||||
RequestWithDate {
|
||||
date: res.current_date,
|
||||
value: res.body,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -463,11 +555,11 @@ struct OAuthToken {
|
||||
async fn oauth_token(
|
||||
code: &str,
|
||||
verifier: &str,
|
||||
) -> Result<OAuthToken, MinecraftAuthenticationError> {
|
||||
) -> Result<RequestWithDate<OAuthToken>, MinecraftAuthenticationError> {
|
||||
let mut query = HashMap::new();
|
||||
query.insert("client_id", "00000000402b5328");
|
||||
query.insert("code", code);
|
||||
query.insert("code_verifier", &*verifier);
|
||||
query.insert("code_verifier", verifier);
|
||||
query.insert("grant_type", "authorization_code");
|
||||
query.insert("redirect_uri", "https://login.live.com/oauth20_desktop.srf");
|
||||
query.insert("scope", "service::user.auth.xboxlive.com::MBI_SSL");
|
||||
@@ -486,6 +578,7 @@ async fn oauth_token(
|
||||
})?;
|
||||
|
||||
let status = res.status();
|
||||
let current_date = get_date_header(res.headers());
|
||||
let text = res.text().await.map_err(|source| {
|
||||
MinecraftAuthenticationError::Request {
|
||||
source,
|
||||
@@ -493,20 +586,25 @@ async fn oauth_token(
|
||||
}
|
||||
})?;
|
||||
|
||||
serde_json::from_str(&text).map_err(|source| {
|
||||
let body = serde_json::from_str(&text).map_err(|source| {
|
||||
MinecraftAuthenticationError::DeserializeResponse {
|
||||
source,
|
||||
raw: text,
|
||||
step: MinecraftAuthStep::GetOAuthToken,
|
||||
status_code: status,
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(RequestWithDate {
|
||||
date: current_date,
|
||||
value: body,
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
async fn oauth_refresh(
|
||||
refresh_token: &str,
|
||||
) -> Result<OAuthToken, MinecraftAuthenticationError> {
|
||||
) -> Result<RequestWithDate<OAuthToken>, MinecraftAuthenticationError> {
|
||||
let mut query = HashMap::new();
|
||||
query.insert("client_id", "00000000402b5328");
|
||||
query.insert("refresh_token", refresh_token);
|
||||
@@ -528,6 +626,7 @@ async fn oauth_refresh(
|
||||
})?;
|
||||
|
||||
let status = res.status();
|
||||
let current_date = get_date_header(res.headers());
|
||||
let text = res.text().await.map_err(|source| {
|
||||
MinecraftAuthenticationError::Request {
|
||||
source,
|
||||
@@ -535,13 +634,18 @@ async fn oauth_refresh(
|
||||
}
|
||||
})?;
|
||||
|
||||
serde_json::from_str(&text).map_err(|source| {
|
||||
let body = serde_json::from_str(&text).map_err(|source| {
|
||||
MinecraftAuthenticationError::DeserializeResponse {
|
||||
source,
|
||||
raw: text,
|
||||
step: MinecraftAuthStep::RefreshOAuthToken,
|
||||
status_code: status,
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(RequestWithDate {
|
||||
date: current_date,
|
||||
value: body,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -562,8 +666,9 @@ async fn sisu_authorize(
|
||||
access_token: &str,
|
||||
device_token: &str,
|
||||
key: &DeviceTokenKey,
|
||||
) -> Result<SisuAuthorize, MinecraftAuthenticationError> {
|
||||
Ok(send_signed_request(
|
||||
current_date: DateTime<Utc>,
|
||||
) -> Result<RequestWithDate<SisuAuthorize>, MinecraftAuthenticationError> {
|
||||
let res = send_signed_request(
|
||||
None,
|
||||
"https://sisu.xboxlive.com/authorize",
|
||||
"/authorize",
|
||||
@@ -587,9 +692,14 @@ async fn sisu_authorize(
|
||||
}),
|
||||
key,
|
||||
MinecraftAuthStep::SisuAuthorize,
|
||||
current_date,
|
||||
)
|
||||
.await?
|
||||
.1)
|
||||
.await?;
|
||||
|
||||
Ok(RequestWithDate {
|
||||
date: res.current_date,
|
||||
value: res.body,
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(key))]
|
||||
@@ -597,8 +707,9 @@ async fn xsts_authorize(
|
||||
authorize: SisuAuthorize,
|
||||
device_token: &str,
|
||||
key: &DeviceTokenKey,
|
||||
) -> Result<DeviceToken, MinecraftAuthenticationError> {
|
||||
Ok(send_signed_request(
|
||||
current_date: DateTime<Utc>,
|
||||
) -> Result<RequestWithDate<DeviceToken>, MinecraftAuthenticationError> {
|
||||
let res = send_signed_request(
|
||||
None,
|
||||
"https://xsts.auth.xboxlive.com/xsts/authorize",
|
||||
"/xsts/authorize",
|
||||
@@ -614,9 +725,14 @@ async fn xsts_authorize(
|
||||
}),
|
||||
key,
|
||||
MinecraftAuthStep::XstsAuthorize,
|
||||
current_date,
|
||||
)
|
||||
.await?
|
||||
.1)
|
||||
.await?;
|
||||
|
||||
Ok(RequestWithDate {
|
||||
date: res.current_date,
|
||||
value: res.body,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -820,6 +936,12 @@ fn generate_key() -> Result<DeviceTokenKey, MinecraftAuthenticationError> {
|
||||
})
|
||||
}
|
||||
|
||||
struct SignedRequestResponse<T> {
|
||||
pub headers: HeaderMap,
|
||||
pub current_date: DateTime<Utc>,
|
||||
pub body: T,
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(key))]
|
||||
async fn send_signed_request<T: DeserializeOwned>(
|
||||
authorization: Option<&str>,
|
||||
@@ -828,14 +950,15 @@ async fn send_signed_request<T: DeserializeOwned>(
|
||||
raw_body: serde_json::Value,
|
||||
key: &DeviceTokenKey,
|
||||
step: MinecraftAuthStep,
|
||||
) -> Result<(HeaderMap, T), MinecraftAuthenticationError> {
|
||||
current_date: DateTime<Utc>,
|
||||
) -> Result<SignedRequestResponse<T>, MinecraftAuthenticationError> {
|
||||
let auth = authorization.map_or(Vec::new(), |v| v.as_bytes().to_vec());
|
||||
|
||||
let body = serde_json::to_vec(&raw_body).map_err(|source| {
|
||||
MinecraftAuthenticationError::SerializeBody { source, step }
|
||||
})?;
|
||||
let time: u128 =
|
||||
{ ((Utc::now().timestamp() as u128) + 11644473600) * 10000000 };
|
||||
{ ((current_date.timestamp() as u128) + 11644473600) * 10000000 };
|
||||
|
||||
use byteorder::WriteBytesExt;
|
||||
let mut buffer = Vec::new();
|
||||
@@ -914,6 +1037,9 @@ async fn send_signed_request<T: DeserializeOwned>(
|
||||
|
||||
let status = res.status();
|
||||
let headers = res.headers().clone();
|
||||
|
||||
let current_date = get_date_header(&headers);
|
||||
|
||||
let body = res.text().await.map_err(|source| {
|
||||
MinecraftAuthenticationError::Request { source, step }
|
||||
})?;
|
||||
@@ -926,7 +1052,21 @@ async fn send_signed_request<T: DeserializeOwned>(
|
||||
status_code: status,
|
||||
}
|
||||
})?;
|
||||
Ok((headers, body))
|
||||
Ok(SignedRequestResponse {
|
||||
headers,
|
||||
current_date,
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
fn get_date_header(headers: &HeaderMap) -> DateTime<Utc> {
|
||||
headers
|
||||
.get(reqwest::header::DATE)
|
||||
.and_then(|x| x.to_str().ok())
|
||||
.and_then(|x| DateTime::parse_from_rfc2822(x).ok())
|
||||
.map(|x| x.with_timezone(&Utc))
|
||||
.unwrap_or(Utc::now())
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
|
||||
@@ -73,10 +73,6 @@ async function loginMinecraft() {
|
||||
<div class="modal-body">
|
||||
<div class="markdown-body">
|
||||
<template v-if="errorType === 'minecraft_auth'">
|
||||
<p>
|
||||
Signing into Microsoft account is a complex task for the launchers, and there are a lot
|
||||
of things can go wrong.
|
||||
</p>
|
||||
<template v-if="metadata.network">
|
||||
<h3>Network issues</h3>
|
||||
<p>
|
||||
@@ -106,23 +102,26 @@ async function loginMinecraft() {
|
||||
</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<h3>Make sure you are signing into the right Microsoft account</h3>
|
||||
<h3>Try another Microsoft account</h3>
|
||||
<p>
|
||||
More often than not, this error is caused by you signing into an incorrect Microsoft
|
||||
account which isn't linked to Minecraft. Double check and try again!
|
||||
Double check you've signed in with the right account. You may own Minecraft on a
|
||||
different Microsoft account.
|
||||
</p>
|
||||
<h3>Try signing in and launching through the official launcher first</h3>
|
||||
<div class="cta-button">
|
||||
<button class="btn btn-primary" :disabled="loadingMinecraft" @click="loginMinecraft">
|
||||
<LogInIcon /> Try another account
|
||||
</button>
|
||||
</div>
|
||||
<h3>Using PC Game Pass, coming from Bedrock, or just bought the game?</h3>
|
||||
<p>
|
||||
If you just bought Minecraft, are coming from the Bedrock Edition world and have never
|
||||
played Java before, or just subscribed to PC Game Pass, you would need to start the
|
||||
game at least once using the
|
||||
<a href="https://www.minecraft.net/en-us/download">official Minecraft Launcher</a>.
|
||||
Once you're done, come back here and sign in!
|
||||
Try signing in with the
|
||||
<a href="https://www.minecraft.net/en-us/download">official Minecraft Launcher</a>
|
||||
first. Once you're done, come back here and sign in!
|
||||
</p>
|
||||
</template>
|
||||
<div class="cta-button">
|
||||
<button class="btn btn-primary" :disabled="loadingMinecraft" @click="loginMinecraft">
|
||||
<LogInIcon /> Sign in to Minecraft
|
||||
<LogInIcon /> Try signing in again
|
||||
</button>
|
||||
</div>
|
||||
<hr />
|
||||
@@ -168,7 +167,7 @@ async function loginMinecraft() {
|
||||
</div>
|
||||
<div class="input-group push-right">
|
||||
<a :href="supportLink" class="btn" @click="errorModal.hide()"><ChatIcon /> Get support</a>
|
||||
<button class="btn" @click="errorModal.hide()"><XIcon /> Close</button>
|
||||
<button class="btn" @clicdck="errorModal.hide()"><XIcon /> Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
Reference in New Issue
Block a user