Skip to content

Commit

Permalink
Fixed Minecraft Education Edition login
Browse files Browse the repository at this point in the history
  • Loading branch information
RaphiMC committed Jul 15, 2024
1 parent 8c79e96 commit 6c6d7ac
Show file tree
Hide file tree
Showing 5 changed files with 16 additions and 12 deletions.
2 changes: 1 addition & 1 deletion src/main/java/net/raphimc/minecraftauth/MinecraftAuth.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public class MinecraftAuth {
public static final AbstractStep<?, StepEduJWT.EduJWT> EDU_DEVICE_CODE_LOGIN = new StepEduJWT(builder()
.withClientId(MicrosoftConstants.EDU_CLIENT_ID).withScope("https://meeservices.minecraft.net/.default offline_access").withOAuthEnvironment(OAuthEnvironment.MICROSOFT_ONLINE_COMMON)
.deviceCode()
.msaTokenStep, "1.20.80", 671, "Windows Desktop Build (Win32)(x64)");
.msaTokenStep, "1.20.13", 594);

@ApiStatus.Experimental
@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@
import java.util.Map;
import java.util.UUID;

import static net.raphimc.minecraftauth.util.TimeUtil.MAX_JWT_CLOCK_SKEW;

public class StepMCChain extends AbstractStep<StepXblXstsToken.XblXsts<?>, StepMCChain.MCChain> {

public static final String MINECRAFT_LOGIN_URL = "https://multiplayer.minecraft.net/authentication";

private static final String MOJANG_PUBLIC_KEY_BASE64 = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAECRXueJeTDqNRRgJi/vlRufByu/2G0i2Ebt6YMar5QX/R0DIIyrJMcUpruK4QveTfJSTp3Shlq4Gk34cD/4GUWwkv0DVuzeuB+tXija7HBxii03NHDbPAD0AKnLr2wdAp";
public static final ECPublicKey MOJANG_PUBLIC_KEY = CryptUtil.publicKeyEcFromBase64(MOJANG_PUBLIC_KEY_BASE64);
private static final int CLOCK_SKEW = 60;

public StepMCChain(final AbstractStep<?, ? extends StepXblXstsToken.XblXsts<?>> prevStep) {
super("mcChain", (AbstractStep<?, StepXblXstsToken.XblXsts<?>>) prevStep);
Expand Down Expand Up @@ -79,9 +80,9 @@ public MCChain applyStep(final ILogger logger, final HttpClient httpClient, fina
throw new IllegalStateException("Invalid chain size");
}

final Jws<Claims> mojangJwt = Jwts.parser().clockSkewSeconds(CLOCK_SKEW).verifyWith(MOJANG_PUBLIC_KEY).build().parseSignedClaims(chain.get(0).getAsString());
final Jws<Claims> mojangJwt = Jwts.parser().clockSkewSeconds(MAX_JWT_CLOCK_SKEW).verifyWith(MOJANG_PUBLIC_KEY).build().parseSignedClaims(chain.get(0).getAsString());
final ECPublicKey mojangJwtPublicKey = CryptUtil.publicKeyEcFromBase64(mojangJwt.getPayload().get("identityPublicKey", String.class));
final Jws<Claims> identityJwt = Jwts.parser().clockSkewSeconds(CLOCK_SKEW).verifyWith(mojangJwtPublicKey).build().parseSignedClaims(chain.get(1).getAsString());
final Jws<Claims> identityJwt = Jwts.parser().clockSkewSeconds(MAX_JWT_CLOCK_SKEW).verifyWith(mojangJwtPublicKey).build().parseSignedClaims(chain.get(1).getAsString());

final Map<String, Object> extraData = identityJwt.getPayload().get("extraData", Map.class);
final String xuid = (String) extraData.get("XUID");
Expand Down Expand Up @@ -191,9 +192,9 @@ public boolean isExpired() {

this.lastExpireCheckTimeMs = System.currentTimeMillis();
try {
final Jws<Claims> mojangJwt = Jwts.parser().clockSkewSeconds(CLOCK_SKEW).verifyWith(MOJANG_PUBLIC_KEY).build().parseSignedClaims(this.mojangJwt);
final Jws<Claims> mojangJwt = Jwts.parser().clockSkewSeconds(MAX_JWT_CLOCK_SKEW).verifyWith(MOJANG_PUBLIC_KEY).build().parseSignedClaims(this.mojangJwt);
final ECPublicKey mojangJwtPublicKey = CryptUtil.publicKeyEcFromBase64(mojangJwt.getPayload().get("identityPublicKey", String.class));
Jwts.parser().clockSkewSeconds(CLOCK_SKEW).verifyWith(mojangJwtPublicKey).build().parseSignedClaims(this.identityJwt);
Jwts.parser().clockSkewSeconds(MAX_JWT_CLOCK_SKEW).verifyWith(mojangJwtPublicKey).build().parseSignedClaims(this.identityJwt);
this.lastExpireCheckResult = false;
} catch (Throwable e) { // Any error -> The jwts are expired or invalid
this.lastExpireCheckResult = true;
Expand Down
11 changes: 6 additions & 5 deletions src/main/java/net/raphimc/minecraftauth/step/edu/StepEduJWT.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ public class StepEduJWT extends AbstractStep<StepMsaToken.MsaToken, StepEduJWT.E
private final String version;
private final int buildNumber;
private final int protocolVersion;
private final String platform;

public StepEduJWT(final AbstractStep<?, StepMsaToken.MsaToken> prevStep, final String version, final int protocolVersion, final String platform) {
public StepEduJWT(final AbstractStep<?, StepMsaToken.MsaToken> prevStep, final String version, final int protocolVersion) {
super("eduJwt", prevStep);

final String[] versionParts = version.split("\\.");
Expand All @@ -48,7 +47,6 @@ public StepEduJWT(final AbstractStep<?, StepMsaToken.MsaToken> prevStep, final S
this.version = version;
this.buildNumber = Integer.parseInt(versionParts[0]) * 10_00_00_00 + Integer.parseInt(versionParts[1]) * 10_00_00 + Integer.parseInt(versionParts[2]) * 10_00;
this.protocolVersion = protocolVersion;
this.platform = platform;
}

@Override
Expand All @@ -61,7 +59,10 @@ public EduJWT applyStep(final ILogger logger, final HttpClient httpClient, final
postData.addProperty("clientVersion", this.protocolVersion);
postData.addProperty("displayVersion", this.version);
postData.addProperty("identityToken", msaToken.getAccessToken());
postData.addProperty("platform", this.platform);
postData.addProperty("locale", "en_US");
postData.addProperty("osVersion", "10.0");
postData.addProperty("platform", "Windows Desktop Build (Win32)(x64)");
postData.addProperty("platformCategory", "desktop");

final PostRequest postRequest = new PostRequest(MINECRAFT_LOGIN_URL);
postRequest.setContent(new JsonContent(postData));
Expand Down Expand Up @@ -106,7 +107,7 @@ protected StepMsaToken.MsaToken prevResult() {

@Override
public boolean isExpired() {
return true; // TODO: Implement
return true; // Can't properly parse and validate the JWT because we don't have the public key
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

import java.io.StringReader;
import java.net.CookieManager;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -99,7 +100,7 @@ public MsaCode applyStep(final ILogger logger, final HttpClient httpClient, fina
case MICROSOFT_ONLINE_COMMON:
case MICROSOFT_ONLINE_CONSUMERS: {
urlPost = config.get("urlPost").getAsString();
urlPost = new URLWrapper(urlPost).setProtocol(authenticationUrl.getProtocol()).setHost(authenticationUrl.getHost()).toURL().toString();
urlPost = new URLWrapper(new URI(urlPost)).setProtocol(authenticationUrl.getProtocol()).setHost(authenticationUrl.getHost()).toURL().toString();
final String sFT = config.get("sFT").getAsString();
final String sFTName = config.get("sFTName").getAsString();
final String sCtx = config.get("sCtx").getAsString();
Expand Down
1 change: 1 addition & 0 deletions src/main/java/net/raphimc/minecraftauth/util/TimeUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
public class TimeUtil {

private static Duration CLIENT_TIME_OFFSET = null;
public static final int MAX_JWT_CLOCK_SKEW = 60;

/**
* Gets the time offset between the client and the microsoft server. This is used to calculate the correct time for authentication and signatures.
Expand Down

0 comments on commit 6c6d7ac

Please sign in to comment.