Skip to content

Commit

Permalink
Move to minecraftauth 4.0
Browse files Browse the repository at this point in the history
Signed-off-by: Joshua Castle <[email protected]>
  • Loading branch information
Kas-tle committed May 2, 2024
1 parent 486e7cb commit fca5b78
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 65 deletions.
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jackson-dataformat-yaml = { group = "com.fasterxml.jackson.dataformat", name = "
jansi = { group = "org.fusesource.jansi", name = "jansi", version = "2.4.1" }
jline-reader = { group = "org.jline", name = "jline-reader", version = "3.24.1" }
jsr305 = { group = "com.google.code.findbugs", name = "jsr305", version = "3.0.2" }
minecraftauth = { group = "net.raphimc", name = "MinecraftAuth", version = "2.1.6" }
minecraftauth = { group = "net.raphimc", name = "MinecraftAuth", version = "4.0.1" }
lombok = { group = "org.projectlombok", name = "lombok", version = "1.18.30" }
checker-qual = { group = "org.checkerframework", name = "checker-qual", version = "3.37.0" }

Expand Down
29 changes: 12 additions & 17 deletions src/main/java/org/cloudburstmc/proxypass/ProxyPass.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;
import net.raphimc.mcauth.MinecraftAuth;
import net.raphimc.mcauth.step.bedrock.StepMCChain;
import net.raphimc.mcauth.step.bedrock.StepPlayFabToken;
import net.raphimc.mcauth.step.msa.StepMsaDeviceCode;
import net.raphimc.mcauth.util.MicrosoftConstants;
import org.apache.http.impl.client.CloseableHttpClient;
import net.lenni0451.commons.httpclient.HttpClient;
import net.raphimc.minecraftauth.MinecraftAuth;
import net.raphimc.minecraftauth.step.bedrock.session.StepFullBedrockSession;
import net.raphimc.minecraftauth.step.msa.StepMsaDeviceCode;
import org.cloudburstmc.nbt.*;
import org.cloudburstmc.netty.channel.raknet.RakChannelFactory;
import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption;
Expand Down Expand Up @@ -176,7 +174,7 @@ public void boot() throws IOException {
log.info("Online mode is enabled. Starting auth process...");
try {
account = getAuthenticatedAccount(saveAuthDetails);
log.info("Successfully logged in as {}", account.mcChain().displayName());
log.info("Successfully logged in as {}", account.bedrockSession().getMcChain().getDisplayName());
} catch (Exception e) {
log.error("Setting to offline mode due to failure to get login chain:", e);
onlineMode = false;
Expand Down Expand Up @@ -339,42 +337,39 @@ public boolean isFull() {

private Account getAuthenticatedAccount(boolean saveAuthDetails) throws Exception {
Path authPath = Paths.get(".").resolve("auth.json");
CloseableHttpClient client = MicrosoftConstants.createHttpClient();
HttpClient client = MinecraftAuth.createHttpClient();
Account account;

if (Files.notExists(authPath) || !Files.isRegularFile(authPath) || !saveAuthDetails) {
StepMCChain.MCChain mcChain = MinecraftAuth.BEDROCK_DEVICE_CODE_LOGIN.getFromInput(client,
StepFullBedrockSession.FullBedrockSession bedrockSession = MinecraftAuth.BEDROCK_DEVICE_CODE_LOGIN.getFromInput(client,
new StepMsaDeviceCode.MsaDeviceCodeCallback(msaDeviceCode -> {
log.info("Go to " + msaDeviceCode.verificationUri());
log.info("Enter code " + msaDeviceCode.userCode());
log.info("Go to " + msaDeviceCode.getVerificationUri());
log.info("Enter code " + msaDeviceCode.getUserCode());

if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
try {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(new StringSelection(msaDeviceCode.userCode()), null);
clipboard.setContents(new StringSelection(msaDeviceCode.getUserCode()), null);
log.info("Copied code to clipboard");
Desktop.getDesktop().browse(new URI(msaDeviceCode.verificationUri()));
Desktop.getDesktop().browse(new URI(msaDeviceCode.getVerificationUri()));
} catch (IOException | URISyntaxException e) {
log.error("Failed to open browser", e);
}
}
}));
StepPlayFabToken.PlayFabToken playFabToken = MinecraftAuth.BEDROCK_PLAY_FAB_TOKEN.getFromInput(client, mcChain.prevResult().fullXblSession());
account = new Account(mcChain, playFabToken);
account = new Account(bedrockSession);

if (saveAuthDetails) {
Files.write(authPath, account.toJson().toString().getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
}

client.close();
return account;
}

String accountString = new String(Files.readAllBytes(authPath), StandardCharsets.UTF_8);
account = new Account(JsonParser.parseString(accountString).getAsJsonObject());
account.refresh(client);
Files.write(authPath, account.toJson().toString().getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
client.close();
return account;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,28 @@
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.log4j.Log4j2;
import net.raphimc.mcauth.MinecraftAuth;
import net.raphimc.mcauth.step.bedrock.StepMCChain;
import net.raphimc.mcauth.step.bedrock.StepPlayFabToken;
import org.apache.http.impl.client.CloseableHttpClient;
import net.lenni0451.commons.httpclient.HttpClient;
import net.raphimc.minecraftauth.MinecraftAuth;
import net.raphimc.minecraftauth.step.bedrock.session.StepFullBedrockSession;

@Log4j2
@Accessors(fluent = true)
@Data
@AllArgsConstructor
// adapted from https://github.com/ViaVersion/ViaProxy/blob/ca40e290092d99abd842f8cce645d8db407de105/src/main/java/net/raphimc/viaproxy/saves/impl/accounts/BedrockAccount.java#L29-L101
public class Account {
private StepMCChain.MCChain mcChain;
private StepPlayFabToken.PlayFabToken playFabToken;
private StepFullBedrockSession.FullBedrockSession bedrockSession;

public Account(JsonObject jsonObject) throws Exception {
this.mcChain = MinecraftAuth.BEDROCK_DEVICE_CODE_LOGIN.fromJson(jsonObject.getAsJsonObject("mc_chain"));
if (jsonObject.has("play_fab_token")) {
try {
this.playFabToken = MinecraftAuth.BEDROCK_PLAY_FAB_TOKEN.fromJson(jsonObject.getAsJsonObject("play_fab_token"));
} catch (Throwable e) {
log.warn("Failed to load PlayFab token for Bedrock account. It will be regenerated.", e);
}
}
this.bedrockSession = MinecraftAuth.BEDROCK_DEVICE_CODE_LOGIN.fromJson(jsonObject);
}

public JsonObject toJson() {
JsonObject jsonObject = new JsonObject();
jsonObject.add("mc_chain", mcChain.toJson());
if (playFabToken != null) {
jsonObject.add("play_fab_token", playFabToken.toJson());
}
JsonObject jsonObject = MinecraftAuth.BEDROCK_DEVICE_CODE_LOGIN.toJson(bedrockSession);
return jsonObject;
}

public boolean refresh(CloseableHttpClient httpClient) throws Exception {
mcChain = MinecraftAuth.BEDROCK_DEVICE_CODE_LOGIN.refresh(httpClient, mcChain);
try {
if (playFabToken == null) {
throw new NullPointerException();
}
playFabToken = MinecraftAuth.BEDROCK_PLAY_FAB_TOKEN.refresh(httpClient, playFabToken);
} catch (Throwable e) {
playFabToken = null;
playFabToken = MinecraftAuth.BEDROCK_PLAY_FAB_TOKEN.getFromInput(httpClient, mcChain.prevResult().fullXblSession());
}
public boolean refresh(HttpClient httpClient) throws Exception {
bedrockSession = MinecraftAuth.BEDROCK_DEVICE_CODE_LOGIN.refresh(httpClient, bedrockSession);
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.cloudburstmc.protocol.bedrock.data.PacketCompressionAlgorithm;
import org.cloudburstmc.protocol.bedrock.packet.*;
import org.cloudburstmc.protocol.bedrock.util.EncryptionUtils;
import org.cloudburstmc.protocol.bedrock.util.JsonUtils;
Expand Down Expand Up @@ -30,8 +31,14 @@ public class DownstreamInitialPacketHandler implements BedrockPacketHandler {

@Override
public PacketSignal handle(NetworkSettingsPacket packet) {
this.session.setCompression(packet.getCompressionAlgorithm());
log.info("Compression algorithm picked {}", packet.getCompressionAlgorithm());
int threshold = packet.getCompressionThreshold();
if (threshold > 0) {
this.session.setCompression(packet.getCompressionAlgorithm());
log.info("Compression threshold set to {}", threshold);
} else {
this.session.setCompression(PacketCompressionAlgorithm.NONE);
log.info("Compression threshold set to 0");
}

this.session.sendPacketImmediately(this.loginPacket);
return PacketSignal.HANDLED;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.fasterxml.jackson.databind.node.JsonNodeType;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import net.raphimc.minecraftauth.step.bedrock.StepMCChain.MCChain;
import org.cloudburstmc.protocol.bedrock.data.PacketCompressionAlgorithm;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacketHandler;
import org.cloudburstmc.protocol.bedrock.packet.LoginPacket;
Expand Down Expand Up @@ -108,7 +109,8 @@ public PacketSignal handle(LoginPacket packet) {

initializeOfflineProxySession();
} else {
this.authData = new AuthData(account.mcChain().displayName(), account.mcChain().id(), account.mcChain().xuid());
MCChain mcChain = account.bedrockSession().getMcChain();
this.authData = new AuthData(mcChain.getDisplayName(), mcChain.getId(), mcChain.getXuid());

initializeOnlineProxySession();
}
Expand Down Expand Up @@ -170,12 +172,13 @@ private void initializeOfflineProxySession() {
private void initializeOnlineProxySession() {
log.debug("Initializing proxy session");
this.proxy.newClient(this.proxy.getTargetAddress(), downstream -> {
MCChain mcChain = account.bedrockSession().getMcChain();
try {
if (mojangPublicKey == null) {
mojangPublicKey = ForgeryUtils.forgeMojangPublicKey();
}
if (onlineLoginChain == null) {
onlineLoginChain = ForgeryUtils.forgeOnlineAuthData(account.mcChain(), mojangPublicKey);
onlineLoginChain = ForgeryUtils.forgeOnlineAuthData(mcChain, mojangPublicKey);
}
} catch (Exception e) {
log.error("Failed to get login chain", e);
Expand All @@ -190,7 +193,7 @@ private void initializeOnlineProxySession() {
downstream,
this.proxy,
this.authData,
new KeyPair(account.mcChain().publicKey(), account.mcChain().privateKey()));
new KeyPair(mcChain.getPublicKey(), mcChain.getPrivateKey()));
this.player = proxySession;

downstream.setPlayer(proxySession);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.cloudburstmc.proxypass.network.bedrock.util;

import lombok.experimental.UtilityClass;
import net.raphimc.mcauth.step.bedrock.StepMCChain;
import net.raphimc.minecraftauth.step.bedrock.StepMCChain;
import org.cloudburstmc.proxypass.network.bedrock.session.Account;
import org.jose4j.json.internal.json_simple.JSONObject;
import org.jose4j.jws.JsonWebSignature;
Expand All @@ -22,6 +22,8 @@
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@UtilityClass
Expand Down Expand Up @@ -58,15 +60,15 @@ public static String forgeOfflineAuthData(KeyPair pair, JSONObject extraData) {
}

public static List<String> forgeOnlineAuthData(StepMCChain.MCChain mcChain, ECPublicKey mojangPublicKey) throws InvalidJwtException, JoseException {
String publicBase64Key = Base64.getEncoder().encodeToString(mcChain.publicKey().getEncoded());
String publicBase64Key = Base64.getEncoder().encodeToString(mcChain.getPublicKey().getEncoded());

// adapted from https://github.com/RaphiMC/ViaBedrock/blob/a771149fe4492e4f1393cad66758313067840fcc/src/main/java/net/raphimc/viabedrock/protocol/packets/LoginPackets.java#L276-L291
JwtConsumer consumer = new JwtConsumerBuilder()
.setAllowedClockSkewInSeconds(60)
.setVerificationKey(mojangPublicKey)
.build();

JsonWebSignature mojangJws = (JsonWebSignature) consumer.process(mcChain.mojangJwt()).getJoseObjects().get(0);
JsonWebSignature mojangJws = (JsonWebSignature) consumer.process(mcChain.getMojangJwt()).getJoseObjects().get(0);

JwtClaims claimsSet = new JwtClaims();
claimsSet.setClaim("certificateAuthority", true);
Expand All @@ -76,13 +78,13 @@ public static List<String> forgeOnlineAuthData(StepMCChain.MCChain mcChain, ECPu

JsonWebSignature selfSignedJws = new JsonWebSignature();
selfSignedJws.setPayload(claimsSet.toJson());
selfSignedJws.setKey(mcChain.privateKey());
selfSignedJws.setKey(mcChain.getPrivateKey());
selfSignedJws.setAlgorithmHeaderValue("ES384");
selfSignedJws.setHeader(HeaderParameterNames.X509_URL, publicBase64Key);

String selfSignedJwt = selfSignedJws.getCompactSerialization();

return new ArrayList<>(List.of(selfSignedJwt, mcChain.mojangJwt(), mcChain.identityJwt()));
return new ArrayList<>(List.of(selfSignedJwt, mcChain.getMojangJwt(), mcChain.getIdentityJwt()));
}

public static String forgeOfflineSkinData(KeyPair pair, JSONObject skinData) {
Expand All @@ -103,12 +105,13 @@ public static String forgeOfflineSkinData(KeyPair pair, JSONObject skinData) {

@SuppressWarnings("unchecked")
public static String forgeOnlineSkinData(Account account, JSONObject skinData, String serverAddress) {
String publicKeyBase64 = Base64.getEncoder().encodeToString(account.mcChain().publicKey().getEncoded());
String publicKeyBase64 = Base64.getEncoder().encodeToString(account.bedrockSession().getMcChain().getPublicKey().getEncoded());

HashMap<String,Object> overrideData = new HashMap<String,Object>();
overrideData.put("PlayFabId", account.playFabToken().playFabId());
overrideData.put("DeviceId", account.mcChain().prevResult().initialXblSession().prevResult2().id());
overrideData.put("ThirdPartyName", account.mcChain().displayName());
overrideData.put("PlayFabId", account.bedrockSession().getPlayFabToken().getPlayFabId().toLowerCase(Locale.ROOT));
overrideData.put("DeviceId", UUID.randomUUID().toString());
overrideData.put("DeviceOS", 1); // Android per MinecraftAuth 4.0
overrideData.put("ThirdPartyName", account.bedrockSession().getMcChain().getDisplayName());
overrideData.put("ServerAddress", serverAddress);

skinData.putAll(overrideData);
Expand All @@ -117,7 +120,7 @@ public static String forgeOnlineSkinData(Account account, JSONObject skinData, S
jws.setAlgorithmHeaderValue("ES384");
jws.setHeader(HeaderParameterNames.X509_URL, publicKeyBase64);
jws.setPayload(skinData.toJSONString());
jws.setKey(account.mcChain().privateKey());
jws.setKey(account.bedrockSession().getMcChain().getPrivateKey());

try {
return jws.getCompactSerialization();
Expand Down

0 comments on commit fca5b78

Please sign in to comment.