Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support authlib-injector accounts on legacy versions #27

Merged
merged 4 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion launcher/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("IgnoreJavaWizard", false);

// Legacy settings
m_settings->registerSetting("OnlineFixes", false);
m_settings->registerSetting("OnlineFixes", true);

// Native library workarounds
m_settings->registerSetting("UseNativeOpenAL", false);
Expand Down
5 changes: 2 additions & 3 deletions launcher/LaunchController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,8 @@ void LaunchController::login()
return;
}

if (m_accountToUse->usesCustomApiServers()) {
MinecraftInstancePtr inst = std::dynamic_pointer_cast<MinecraftInstance>(m_instance);

MinecraftInstancePtr inst = std::dynamic_pointer_cast<MinecraftInstance>(m_instance);
if (m_accountToUse->usesCustomApiServers() && !inst->shouldApplyOnlineFixes()) {
bool authlibInjectorInstalled = false;
const auto& agents = inst->getPackProfile()->getProfile()->getAgents();
for (const auto& agent : agents) {
Expand Down
1 change: 1 addition & 0 deletions libraries/launcher/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ set(LEGACY_SRC
legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java
legacy/org/prismlauncher/legacy/fix/online/SkinFix.java
legacy/org/prismlauncher/legacy/utils/Base64.java
legacy/org/prismlauncher/legacy/utils/api/ApiServers.java
legacy/org/prismlauncher/legacy/utils/api/MojangApi.java
legacy/org/prismlauncher/legacy/utils/api/Texture.java
legacy/org/prismlauncher/legacy/utils/json/JsonParseException.java
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,32 +35,80 @@

package org.prismlauncher.legacy.fix.online;

import org.prismlauncher.legacy.utils.api.ApiServers;
import org.prismlauncher.legacy.utils.api.MojangApi;
import org.prismlauncher.legacy.utils.url.ByteArrayUrlConnection;
import org.prismlauncher.legacy.utils.url.UrlUtils;

import java.io.IOException;
import java.net.MalformedURLException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;

public final class OnlineModeFix {
public static URLConnection openConnection(URL address, Proxy proxy) throws IOException {
// we start with "http://www.minecraft.net/game/joinserver.jsp?user=..."
if (!(address.getHost().equals("www.minecraft.net") && address.getPath().equals("/game/joinserver.jsp")))
if (!(address.getHost().equals("www.minecraft.net") && address.getPath().equals("/game/joinserver.jsp"))) {
return null;
}

Map<String, String> params = new HashMap<>();
String query = address.getQuery();
String[] entries = query.split("&");
for (String entry : entries) {
String[] pair = entry.split("=");
if (pair.length == 2) {
params.put(pair[0], pair[1]);
}
}

// change it to "https://session.minecraft.net/game/joinserver.jsp?user=..."
// this seems to be the modern version of the same endpoint...
// maybe Mojang planned to patch old versions of the game to use it
// if it ever disappears this should be changed to use sessionserver.mojang.com/session/minecraft/join
// which of course has a different usage requiring JSON serialisation...
URL url;
try {
url = new URL("https", "session.minecraft.net", address.getPort(), address.getFile());
} catch (MalformedURLException e) {
throw new AssertionError("url should be valid", e);
String user = params.get("user");
if (user == null) {
throw new AssertionError("missing user");
}
String serverId = params.get("serverId");
if (serverId == null) {
throw new AssertionError("missing serverId");
}
String sessionId = params.get("sessionId");
if (sessionId == null) {
throw new AssertionError("missing sessionId");
}

// sessionId has the form:
// token:<accessToken>:<player UUID>
String accessToken = sessionId.split(":")[1];

return UrlUtils.openConnection(url, proxy);
String uuid = null;
uuid = MojangApi.getUuid(user, proxy);
if (uuid == null) {
return new ByteArrayUrlConnection(("Couldn't find UUID of " + user).getBytes("utf-8"));
}

URL url = new URL(ApiServers.getSessionURL() + "/session/minecraft/join");
HttpURLConnection connection = (HttpURLConnection) UrlUtils.openConnection(url, proxy);
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
try (OutputStream os = connection.getOutputStream()) {
String payload = "{"
+ "\"accessToken\": \"" + accessToken + "\","
+ "\"selectedProfile\": \"" + uuid + "\","
+ "\"serverId\": \"" + serverId + "\""
+ "}";
os.write(payload.getBytes("utf-8"));
}
int responseCode = connection.getResponseCode();

if (responseCode == 204) {
return new ByteArrayUrlConnection("OK".getBytes("utf-8"));
} else {
return new ByteArrayUrlConnection("Bad login".getBytes("utf-8"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ static URLConnection openConnection(URL address, Proxy proxy) throws IOException
if (capeOwner != null) {
// since we do not need to process the image, open a direct connection bypassing
// Handler
Texture texture = MojangApi.getTexture(MojangApi.getUuid(capeOwner), "CAPE");
Texture texture = MojangApi.getTexture(MojangApi.getUuid(capeOwner, proxy), "CAPE");
if (texture == null)
return null;

Expand All @@ -91,7 +91,7 @@ static URLConnection openConnection(URL address, Proxy proxy) throws IOException
}

private static URLConnection getSkinConnection(String owner, Proxy proxy) throws IOException {
Texture texture = MojangApi.getTexture(MojangApi.getUuid(owner), "SKIN");
Texture texture = MojangApi.getTexture(MojangApi.getUuid(owner, proxy), "SKIN");
if (texture == null)
return null;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Fjord Launcher - Minecraft Launcher
* Copyright (C) 2024 Evan Goode <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package org.prismlauncher.legacy.utils.api;

public class ApiServers {
public static String getAuthURL() {
return getPropertyWithFallback("minecraft.api.auth.host", "https://authserver.mojang.com");
}
public static String getAccountURL() {
return getPropertyWithFallback("minecraft.api.account.host", "https://api.mojang.com");
}
public static String getSessionURL() {
return getPropertyWithFallback("minecraft.api.session.host", "https://sessionserver.mojang.com");
}
public static String getServicesURL() {
return getPropertyWithFallback("minecraft.api.services.host", "https://api.minecraftservices.com");
}
private static String getPropertyWithFallback(String key, String fallback) {
String value = System.getProperty(key);
if (value == null) {
return fallback;
}
return value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,38 @@
package org.prismlauncher.legacy.utils.api;

import org.prismlauncher.legacy.utils.Base64;
import org.prismlauncher.legacy.utils.api.ApiServers;
import org.prismlauncher.legacy.utils.json.JsonParser;
import org.prismlauncher.legacy.utils.url.UrlUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.util.List;
import java.util.Map;

/**
* Basic wrapper for Mojang's Minecraft API.
*/
@SuppressWarnings("unchecked")
public final class MojangApi {
public static String getUuid(String username) throws IOException {
try (InputStream in = new URL("https://api.mojang.com/users/profiles/minecraft/" + username).openStream()) {
Map<String, Object> map = (Map<String, Object>) JsonParser.parse(in);
return (String) map.get("id");
public static String getUuid(String username, Proxy proxy) throws IOException {
URL url = new URL(ApiServers.getAccountURL() + "/profiles/minecraft");
HttpURLConnection connection = (HttpURLConnection) UrlUtils.openConnection(url, proxy);
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
try (OutputStream os = connection.getOutputStream()) {
String payload = "[\"" + username + "\"]";
os.write(payload.getBytes("utf-8"));
}
try (InputStream in = connection.getInputStream()) {
List<Map<String, Object>> list = (List<Map<String, Object>>) JsonParser.parse(in);
return (String) list.get(0).get("id");
}
}

Expand Down Expand Up @@ -79,7 +95,7 @@ public static Texture getTexture(String player, String id) throws IOException {
}

public static Map<String, Object> getTextures(String player) throws IOException {
try (InputStream profileIn = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + player).openStream()) {
try (InputStream profileIn = new URL(ApiServers.getSessionURL() + "/session/minecraft/profile/" + player).openStream()) {
Map<String, Object> profile = (Map<String, Object>) JsonParser.parse(profileIn);

for (Map<String, Object> property : (Iterable<Map<String, Object>>) profile.get("properties")) {
Expand Down
Loading