Switch to PKSE OAuth impl (#1146)
* Auth pkse * add additional fields * fix actions * fix lint * Purge broken auth + bump version
This commit is contained in:
parent
e9e99956ad
commit
deedf4fc8b
2
.github/workflows/tauri-build.yml
vendored
2
.github/workflows/tauri-build.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt, clippy
|
||||
targets: aarch64-apple-darwin
|
||||
targets: aarch64-apple-darwin, x86_64-apple-darwin
|
||||
|
||||
- name: Rust setup
|
||||
if: "!startsWith(matrix.platform, 'macos')"
|
||||
|
||||
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -5074,7 +5074,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "theseus"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"async-recursion",
|
||||
"async-tungstenite",
|
||||
@ -5126,7 +5126,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "theseus_gui"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"cocoa 0.25.0",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "theseus"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
authors = ["Jai A <jaiagr+gpg@pm.me>"]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ use crate::State;
|
||||
use base64::prelude::{BASE64_STANDARD, BASE64_URL_SAFE_NO_PAD};
|
||||
use base64::Engine;
|
||||
use byteorder::BigEndian;
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use chrono::{DateTime, Duration, NaiveDate, Utc};
|
||||
use p256::ecdsa::signature::Signer;
|
||||
use p256::ecdsa::{Signature, SigningKey, VerifyingKey};
|
||||
use p256::pkcs8::{DecodePrivateKey, EncodePrivateKey, LineEnding};
|
||||
@ -15,6 +15,7 @@ use reqwest::Response;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use sha2::Digest;
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
use uuid::Uuid;
|
||||
@ -84,6 +85,7 @@ pub struct SaveDeviceToken {
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct MinecraftLoginFlow {
|
||||
pub verifier: String,
|
||||
pub challenge: String,
|
||||
pub session_id: String,
|
||||
pub redirect_uri: String,
|
||||
@ -157,6 +159,22 @@ impl MinecraftAuthStore {
|
||||
}
|
||||
|
||||
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.token.issue_instant
|
||||
< DateTime::parse_from_rfc3339(
|
||||
"2024-04-25T23:59:59.999999999Z",
|
||||
)
|
||||
.unwrap()
|
||||
{
|
||||
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)
|
||||
@ -192,11 +210,17 @@ impl MinecraftAuthStore {
|
||||
pub async fn login_begin(&mut self) -> crate::Result<MinecraftLoginFlow> {
|
||||
let (key, token) = self.refresh_and_get_device_token().await?;
|
||||
|
||||
let challenge = generate_oauth_challenge();
|
||||
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).await?;
|
||||
|
||||
Ok(MinecraftLoginFlow {
|
||||
verifier,
|
||||
challenge,
|
||||
session_id,
|
||||
redirect_uri: redirect_uri.msa_oauth_redirect,
|
||||
@ -211,7 +235,7 @@ impl MinecraftAuthStore {
|
||||
) -> crate::Result<Credentials> {
|
||||
let (key, token) = self.refresh_and_get_device_token().await?;
|
||||
|
||||
let oauth_token = oauth_token(code, &flow.challenge).await?;
|
||||
let oauth_token = oauth_token(code, &flow.verifier).await?;
|
||||
let sisu_authorize = sisu_authorize(
|
||||
Some(&flow.session_id),
|
||||
&oauth_token.access_token,
|
||||
@ -403,13 +427,14 @@ async fn sisu_authenticate(
|
||||
],
|
||||
"Query": {
|
||||
"code_challenge": challenge,
|
||||
"code_challenge_method": "plain",
|
||||
"state": "",
|
||||
"code_challenge_method": "S256",
|
||||
"state": generate_oauth_challenge(),
|
||||
"prompt": "select_account"
|
||||
},
|
||||
"RedirectUri": REDIRECT_URL,
|
||||
"Sandbox": "RETAIL",
|
||||
"TokenType": "code",
|
||||
"TitleId": 1794566092,
|
||||
}),
|
||||
key,
|
||||
MinecraftAuthStep::SisuAuthenicate,
|
||||
@ -439,12 +464,12 @@ struct OAuthToken {
|
||||
#[tracing::instrument]
|
||||
async fn oauth_token(
|
||||
code: &str,
|
||||
challenge: &str,
|
||||
verifier: &str,
|
||||
) -> Result<OAuthToken, MinecraftAuthenticationError> {
|
||||
let mut query = HashMap::new();
|
||||
query.insert("client_id", "00000000402b5328");
|
||||
query.insert("code", code);
|
||||
query.insert("code_verifier", challenge);
|
||||
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");
|
||||
@ -555,6 +580,8 @@ async fn sisu_authorize(
|
||||
"Sandbox": "RETAIL",
|
||||
"SessionId": session_id,
|
||||
"SiteName": "user.auth.xboxlive.com",
|
||||
"RelyingParty": "http://xboxlive.com",
|
||||
"UseModernGamertag": "true"
|
||||
}),
|
||||
key,
|
||||
MinecraftAuthStep::SisuAuthorize,
|
||||
@ -854,9 +881,12 @@ async fn send_signed_request<T: DeserializeOwned>(
|
||||
.post(url)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Accept", "application/json")
|
||||
.header("x-xbl-contract-version", "1")
|
||||
.header("signature", &signature);
|
||||
|
||||
if url != "https://sisu.xboxlive.com/authorize" {
|
||||
request = request.header("x-xbl-contract-version", "1");
|
||||
}
|
||||
|
||||
if let Some(auth) = authorization {
|
||||
request = request.header("Authorization", auth);
|
||||
}
|
||||
@ -885,6 +915,6 @@ async fn send_signed_request<T: DeserializeOwned>(
|
||||
fn generate_oauth_challenge() -> String {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let bytes: Vec<u8> = (0..32).map(|_| rng.gen()).collect();
|
||||
let bytes: Vec<u8> = (0..64).map(|_| rng.gen()).collect();
|
||||
bytes.iter().map(|byte| format!("{:02x}", byte)).collect()
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "theseus_gui",
|
||||
"private": true,
|
||||
"version": "0.7.1",
|
||||
"version": "0.7.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "theseus_gui"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
license = ""
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "Modrinth App",
|
||||
"version": "0.7.1"
|
||||
"version": "0.7.2"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
|
||||
@ -23,7 +23,10 @@ defineExpose({
|
||||
supportLink.value =
|
||||
'https://support.modrinth.com/en/articles/9038231-minecraft-sign-in-issues'
|
||||
|
||||
if (errorVal.message.includes('existing connection was forcibly closed')) {
|
||||
if (
|
||||
errorVal.message.includes('existing connection was forcibly closed') ||
|
||||
errorVal.message.includes('error sending request for url')
|
||||
) {
|
||||
metadata.value.network = true
|
||||
}
|
||||
if (errorVal.message.includes('because the target machine actively refused it')) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user