Yggdrasil Login (#229)
This commit is contained in:
@@ -94,6 +94,8 @@
|
|||||||
- `Random Teleport, Hit Chance, Random Delay` (Removed from Meteor in [8722e](https://github.com/MeteorDevelopment/meteor-client/commit/8722ef565afa02ca4b6d9710a20fc9fcfd97bf05))
|
- `Random Teleport, Hit Chance, Random Delay` (Removed from Meteor in [8722e](https://github.com/MeteorDevelopment/meteor-client/commit/8722ef565afa02ca4b6d9710a20fc9fcfd97bf05))
|
||||||
- AimAssist
|
- AimAssist
|
||||||
- `Fov filter`
|
- `Fov filter`
|
||||||
|
- Alts
|
||||||
|
- `Yggdrasil Login`
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
- `.center`
|
- `.center`
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package anticope.rejects.mixin;
|
||||||
|
|
||||||
|
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
||||||
|
import com.mojang.authlib.minecraft.UserApiService;
|
||||||
|
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.network.SocialInteractionsManager;
|
||||||
|
import net.minecraft.client.report.AbuseReportContext;
|
||||||
|
import net.minecraft.client.texture.PlayerSkinProvider;
|
||||||
|
import net.minecraft.client.util.ProfileKeys;
|
||||||
|
import net.minecraft.client.util.Session;
|
||||||
|
import net.minecraft.network.encryption.SignatureVerifier;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Mutable;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
@Mixin(MinecraftClient.class)
|
||||||
|
public interface MinecraftClientAccessor {
|
||||||
|
@Mutable
|
||||||
|
@Accessor("session")
|
||||||
|
void setSession(Session session);
|
||||||
|
|
||||||
|
@Mutable
|
||||||
|
@Accessor("sessionService")
|
||||||
|
void setSessionService(MinecraftSessionService sessionService);
|
||||||
|
|
||||||
|
@Mutable
|
||||||
|
@Accessor("authenticationService")
|
||||||
|
void setAuthenticationService(YggdrasilAuthenticationService authenticationService);
|
||||||
|
|
||||||
|
@Mutable
|
||||||
|
@Accessor("servicesSignatureVerifier")
|
||||||
|
void setServicesSignatureVerifier(SignatureVerifier servicesSignatureVerifier);
|
||||||
|
|
||||||
|
@Mutable
|
||||||
|
@Accessor("skinProvider")
|
||||||
|
void setSkinProvider(PlayerSkinProvider skinProvider);
|
||||||
|
|
||||||
|
@Mutable
|
||||||
|
@Accessor("socialInteractionsManager")
|
||||||
|
void setSocialInteractionsManager(SocialInteractionsManager socialInteractionsManager);
|
||||||
|
|
||||||
|
@Mutable
|
||||||
|
@Accessor("userApiService")
|
||||||
|
void setUserApiService(UserApiService userApiService);
|
||||||
|
|
||||||
|
@Mutable
|
||||||
|
@Accessor("abuseReportContext")
|
||||||
|
void setAbuseReportContext(AbuseReportContext abuseReportContext);
|
||||||
|
|
||||||
|
@Mutable
|
||||||
|
@Accessor("profileKeys")
|
||||||
|
void setProfileKeys(ProfileKeys profileKeys);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package anticope.rejects.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.client.texture.PlayerSkinProvider;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
@Mixin(PlayerSkinProvider.class)
|
||||||
|
public interface PlayerSkinProviderAccessor {
|
||||||
|
@Accessor("skinCacheDir")
|
||||||
|
File getSkinCacheDir();
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package anticope.rejects.mixin.meteor;
|
||||||
|
|
||||||
|
import anticope.rejects.utils.accounts.CustomYggdrasilAccount;
|
||||||
|
import meteordevelopment.meteorclient.systems.accounts.Account;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||||
|
|
||||||
|
@Mixin(value = Account.class, remap = false)
|
||||||
|
public class AccountMixin {
|
||||||
|
@ModifyArg(method = "toTag", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/NbtCompound;putString(Ljava/lang/String;Ljava/lang/String;)V", ordinal = 0), index = 1)
|
||||||
|
private String putString(String key) {
|
||||||
|
if ((Object) this instanceof CustomYggdrasilAccount) return "Yggdrasil";
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package anticope.rejects.mixin.meteor;
|
||||||
|
|
||||||
|
import anticope.rejects.utils.accounts.CustomYggdrasilAccount;
|
||||||
|
import meteordevelopment.meteorclient.systems.accounts.Account;
|
||||||
|
import meteordevelopment.meteorclient.systems.accounts.Accounts;
|
||||||
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import net.minecraft.nbt.NbtElement;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||||
|
|
||||||
|
@Mixin(value = Accounts.class, remap = false)
|
||||||
|
public class AccountsMixin {
|
||||||
|
@Inject(method = "lambda$fromTag$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/NbtCompound;getString(Ljava/lang/String;)Ljava/lang/String;"), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true)
|
||||||
|
private static void onFromTag(NbtElement tag1, CallbackInfoReturnable<Account<?>> cir, NbtCompound t) {
|
||||||
|
if (t.getString("type").equals("Yggdrasil")) {
|
||||||
|
Account<CustomYggdrasilAccount> account = new CustomYggdrasilAccount(null, null, null).fromTag(t);
|
||||||
|
if (account.fetchInfo()) cir.setReturnValue(account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package anticope.rejects.mixin.meteor;
|
||||||
|
|
||||||
|
import anticope.rejects.utils.accounts.AddCustomYggdrasilAccountScreen;
|
||||||
|
import meteordevelopment.meteorclient.gui.GuiTheme;
|
||||||
|
import meteordevelopment.meteorclient.gui.WindowScreen;
|
||||||
|
import meteordevelopment.meteorclient.gui.screens.AccountsScreen;
|
||||||
|
import meteordevelopment.meteorclient.gui.widgets.WWidget;
|
||||||
|
import meteordevelopment.meteorclient.gui.widgets.containers.WContainer;
|
||||||
|
import meteordevelopment.meteorclient.gui.widgets.containers.WHorizontalList;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||||
|
|
||||||
|
import static meteordevelopment.meteorclient.MeteorClient.mc;
|
||||||
|
|
||||||
|
@Mixin(value = AccountsScreen.class, remap = false)
|
||||||
|
public abstract class AccountsScreenMixin extends WindowScreen {
|
||||||
|
public AccountsScreenMixin(GuiTheme theme, WWidget icon, String title) {
|
||||||
|
super(theme, icon, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
protected abstract void addButton(WContainer c, String text, Runnable action);
|
||||||
|
|
||||||
|
@Inject(method = "initWidgets", at = @At(value = "INVOKE", target = "Lmeteordevelopment/meteorclient/gui/screens/AccountsScreen;addButton(Lmeteordevelopment/meteorclient/gui/widgets/containers/WContainer;Ljava/lang/String;Ljava/lang/Runnable;)V", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD)
|
||||||
|
private void afterAddCrackedButton(CallbackInfo info, WHorizontalList l) {
|
||||||
|
addButton(l, "Yggdrasil", () -> mc.setScreen(new AddCustomYggdrasilAccountScreen(theme, (AccountsScreen) (Object) this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package anticope.rejects.mixin.meteor;
|
||||||
|
|
||||||
|
import anticope.rejects.utils.accounts.CustomYggdrasilAccount;
|
||||||
|
import meteordevelopment.meteorclient.gui.widgets.WAccount;
|
||||||
|
import meteordevelopment.meteorclient.systems.accounts.Account;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||||
|
|
||||||
|
@Mixin(value = WAccount.class, remap = false)
|
||||||
|
public class WAccountMixin {
|
||||||
|
@Shadow @Final private Account<?> account;
|
||||||
|
|
||||||
|
@ModifyArg(method = "init", at = @At(value = "INVOKE", target = "Lmeteordevelopment/meteorclient/gui/GuiTheme;label(Ljava/lang/String;)Lmeteordevelopment/meteorclient/gui/widgets/WLabel;",ordinal = 1))
|
||||||
|
private String accountName(String text) {
|
||||||
|
if (account instanceof CustomYggdrasilAccount) return "(Yggdrasil)";
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package anticope.rejects.utils.accounts;
|
||||||
|
|
||||||
|
import anticope.rejects.mixin.MinecraftClientAccessor;
|
||||||
|
import anticope.rejects.mixin.PlayerSkinProviderAccessor;
|
||||||
|
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||||
|
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
||||||
|
import com.mojang.authlib.minecraft.UserApiService;
|
||||||
|
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||||
|
import net.minecraft.client.network.SocialInteractionsManager;
|
||||||
|
import net.minecraft.client.report.AbuseReportContext;
|
||||||
|
import net.minecraft.client.report.ReporterEnvironment;
|
||||||
|
import net.minecraft.client.texture.PlayerSkinProvider;
|
||||||
|
import net.minecraft.client.util.ProfileKeys;
|
||||||
|
import net.minecraft.client.util.Session;
|
||||||
|
import net.minecraft.network.encryption.SignatureVerifier;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import static meteordevelopment.meteorclient.MeteorClient.mc;
|
||||||
|
|
||||||
|
public class AccountUtils {
|
||||||
|
public static MinecraftSessionService applyLoginEnvironment(YggdrasilAuthenticationService authService, MinecraftSessionService sessService, Session session) {
|
||||||
|
File skinDir = ((PlayerSkinProviderAccessor) mc.getSkinProvider()).getSkinCacheDir();
|
||||||
|
((MinecraftClientAccessor) mc).setSession(session);
|
||||||
|
((MinecraftClientAccessor) mc).setAuthenticationService(authService);
|
||||||
|
((MinecraftClientAccessor) mc).setSessionService(sessService);
|
||||||
|
((MinecraftClientAccessor) mc).setServicesSignatureVerifier(SignatureVerifier.create(authService.getServicesKey()));
|
||||||
|
((MinecraftClientAccessor) mc).setSkinProvider(new PlayerSkinProvider(mc.getTextureManager(), skinDir, sessService));
|
||||||
|
UserApiService apiService = createUserApiService(authService, session);
|
||||||
|
((MinecraftClientAccessor) mc).setUserApiService(apiService);
|
||||||
|
((MinecraftClientAccessor) mc).setSocialInteractionsManager(new SocialInteractionsManager(mc, apiService));
|
||||||
|
((MinecraftClientAccessor) mc).setProfileKeys(ProfileKeys.create(apiService, session, mc.runDirectory.toPath()));
|
||||||
|
((MinecraftClientAccessor) mc).setAbuseReportContext(AbuseReportContext.create(ReporterEnvironment.ofIntegratedServer(), apiService));
|
||||||
|
return sessService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UserApiService createUserApiService(YggdrasilAuthenticationService authService, Session session) {
|
||||||
|
try {
|
||||||
|
return authService.createUserApiService(session.getAccessToken());
|
||||||
|
} catch (AuthenticationException e) {
|
||||||
|
return UserApiService.OFFLINE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package anticope.rejects.utils.accounts;
|
||||||
|
|
||||||
|
import meteordevelopment.meteorclient.gui.GuiTheme;
|
||||||
|
import meteordevelopment.meteorclient.gui.screens.AccountsScreen;
|
||||||
|
import meteordevelopment.meteorclient.gui.screens.AddAccountScreen;
|
||||||
|
import meteordevelopment.meteorclient.gui.widgets.containers.WTable;
|
||||||
|
import meteordevelopment.meteorclient.gui.widgets.input.WTextBox;
|
||||||
|
import meteordevelopment.meteorclient.systems.accounts.Accounts;
|
||||||
|
|
||||||
|
public class AddCustomYggdrasilAccountScreen extends AddAccountScreen {
|
||||||
|
public AddCustomYggdrasilAccountScreen(GuiTheme theme, AccountsScreen parent) {
|
||||||
|
super(theme, "Add Yggdrasil Account", parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initWidgets() {
|
||||||
|
WTable t = add(theme.table()).widget();
|
||||||
|
|
||||||
|
// Email
|
||||||
|
t.add(theme.label("Username / Email: "));
|
||||||
|
WTextBox username = t.add(theme.textBox("")).minWidth(400).expandX().widget();
|
||||||
|
username.setFocused(true);
|
||||||
|
t.row();
|
||||||
|
|
||||||
|
// Password
|
||||||
|
t.add(theme.label("Password: "));
|
||||||
|
WTextBox password = t.add(theme.textBox("")).minWidth(400).expandX().widget();
|
||||||
|
t.row();
|
||||||
|
|
||||||
|
// Password
|
||||||
|
t.add(theme.label("Server: "));
|
||||||
|
WTextBox server = t.add(theme.textBox("")).minWidth(400).expandX().widget();
|
||||||
|
t.row();
|
||||||
|
|
||||||
|
// Add
|
||||||
|
add = t.add(theme.button("Add")).expandX().widget();
|
||||||
|
add.action = () -> {
|
||||||
|
CustomYggdrasilAccount account = new CustomYggdrasilAccount(username.get(), password.get(), server.get());
|
||||||
|
if (!username.get().isEmpty() && !password.get().isEmpty() && !Accounts.get().exists(account)) {
|
||||||
|
AccountsScreen.addAccount(this, parent, account);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enterAction = add.action;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package anticope.rejects.utils.accounts;
|
||||||
|
|
||||||
|
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||||
|
import meteordevelopment.meteorclient.MeteorClient;
|
||||||
|
import meteordevelopment.meteorclient.mixin.MinecraftClientAccessor;
|
||||||
|
import meteordevelopment.meteorclient.systems.accounts.Account;
|
||||||
|
import meteordevelopment.meteorclient.systems.accounts.AccountType;
|
||||||
|
import meteordevelopment.meteorclient.utils.misc.NbtException;
|
||||||
|
import net.minecraft.client.util.Session;
|
||||||
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
|
||||||
|
import static meteordevelopment.meteorclient.MeteorClient.mc;
|
||||||
|
|
||||||
|
public class CustomYggdrasilAccount extends Account<CustomYggdrasilAccount> {
|
||||||
|
private String password, server;
|
||||||
|
|
||||||
|
public CustomYggdrasilAccount(String name, String password, String server) {
|
||||||
|
super(AccountType.Cracked, name);
|
||||||
|
this.password = password;
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean fetchInfo() {
|
||||||
|
try {
|
||||||
|
Session session = CustomYggdrasilLogin.login(name, password, server);
|
||||||
|
|
||||||
|
cache.username = session.getUsername();
|
||||||
|
cache.uuid = session.getUuid();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (AuthenticationException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean login() {
|
||||||
|
try {
|
||||||
|
Session session = CustomYggdrasilLogin.login(name, password, server);
|
||||||
|
CustomYggdrasilLogin.LocalYggdrasilAuthenticationService service = new CustomYggdrasilLogin.LocalYggdrasilAuthenticationService(((MinecraftClientAccessor) mc).getProxy(), server);
|
||||||
|
CustomYggdrasilLogin.applyYggdrasilAccount(service, session);
|
||||||
|
cache.username = session.getUsername();
|
||||||
|
cache.loadHead();
|
||||||
|
return true;
|
||||||
|
} catch (AuthenticationException e) {
|
||||||
|
if (e.getMessage().contains("Invalid username or password") || e.getMessage().contains("account migrated"))
|
||||||
|
MeteorClient.LOG.error("Wrong password.");
|
||||||
|
else MeteorClient.LOG.error("Failed to contact the authentication server.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NbtCompound toTag() {
|
||||||
|
NbtCompound tag = super.toTag();
|
||||||
|
|
||||||
|
tag.putString("password", password);
|
||||||
|
tag.putString("server", server);
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomYggdrasilAccount fromTag(NbtCompound tag) {
|
||||||
|
super.fromTag(tag);
|
||||||
|
if (!tag.contains("password")) throw new NbtException();
|
||||||
|
|
||||||
|
password = tag.getString("password");
|
||||||
|
server = tag.getString("server");
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof CustomYggdrasilAccount)) return false;
|
||||||
|
return ((CustomYggdrasilAccount) o).name.equals(this.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
// Credit to https://github.com/IAFEnvoy/AccountSwitcher
|
||||||
|
|
||||||
|
package anticope.rejects.utils.accounts;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.gson.*;
|
||||||
|
import com.mojang.authlib.Environment;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||||
|
import com.mojang.authlib.minecraft.InsecureTextureException;
|
||||||
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||||
|
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
||||||
|
import com.mojang.authlib.properties.Property;
|
||||||
|
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||||
|
import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService;
|
||||||
|
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||||
|
import com.mojang.util.UUIDTypeAdapter;
|
||||||
|
import meteordevelopment.meteorclient.utils.network.Http;
|
||||||
|
import net.minecraft.client.util.Session;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class CustomYggdrasilLogin {
|
||||||
|
public static Session login(String name, String password, String server) throws AuthenticationException {
|
||||||
|
try {
|
||||||
|
String url = server + "/authserver/authenticate";
|
||||||
|
JsonObject agent = new JsonObject();
|
||||||
|
agent.addProperty("name", "Minecraft");
|
||||||
|
agent.addProperty("version", 1);
|
||||||
|
|
||||||
|
JsonObject root = new JsonObject();
|
||||||
|
root.add("agent", agent);
|
||||||
|
root.addProperty("username", name);
|
||||||
|
root.addProperty("password", password);
|
||||||
|
|
||||||
|
String data = Http.post(url).bodyJson(root).sendString();
|
||||||
|
JsonObject json = JsonParser.parseString(data).getAsJsonObject();
|
||||||
|
if (json.has("error")) {
|
||||||
|
throw new AuthenticationException(json.get("errorMessage").getAsString());
|
||||||
|
}
|
||||||
|
String token = json.get("accessToken").getAsString();
|
||||||
|
String uuid = json.get("selectedProfile").getAsJsonObject().get("id").getAsString();
|
||||||
|
String username = json.get("selectedProfile").getAsJsonObject().get("name").getAsString();
|
||||||
|
return new Session(username, uuid, token, Optional.empty(), Optional.empty(), Session.AccountType.MOJANG);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new AuthenticationException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void applyYggdrasilAccount(LocalYggdrasilAuthenticationService authService, Session session) {
|
||||||
|
MinecraftSessionService service = new LocalYggdrasilMinecraftSessionService(authService, authService.server);
|
||||||
|
AccountUtils.applyLoginEnvironment(authService, service, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LocalYggdrasilApi implements Environment {
|
||||||
|
private final String url;
|
||||||
|
|
||||||
|
public LocalYggdrasilApi(String serverUrl) {
|
||||||
|
this.url = serverUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthHost() {
|
||||||
|
return url + "/authserver";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAccountsHost() {
|
||||||
|
return url + "/api";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSessionHost() {
|
||||||
|
return url + "/sessionserver";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getServicesHost() {
|
||||||
|
return url + "/minecraftservices";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Custom-Yggdrasil";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String asString() {
|
||||||
|
return new StringJoiner(", ", "", "")
|
||||||
|
.add("authHost='" + getAuthHost() + "'")
|
||||||
|
.add("accountsHost='" + getAccountsHost() + "'")
|
||||||
|
.add("sessionHost='" + getSessionHost() + "'")
|
||||||
|
.add("servicesHost='" + getServicesHost() + "'")
|
||||||
|
.add("name='" + getName() + "'")
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LocalYggdrasilMinecraftSessionService extends YggdrasilMinecraftSessionService {
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger();
|
||||||
|
private final PublicKey publicKey;
|
||||||
|
private final Gson gson = new GsonBuilder().registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).create();
|
||||||
|
|
||||||
|
public LocalYggdrasilMinecraftSessionService(YggdrasilAuthenticationService service, String serverUrl) {
|
||||||
|
super(service, new LocalYggdrasilApi(serverUrl));
|
||||||
|
String data = Http.get(serverUrl).sendString();
|
||||||
|
JsonObject json = JsonParser.parseString(data).getAsJsonObject();
|
||||||
|
this.publicKey = getPublicKey(json.get("signaturePublickey").getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PublicKey getPublicKey(String key) {
|
||||||
|
key = key.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "");
|
||||||
|
try {
|
||||||
|
byte[] byteKey = Base64.getDecoder().decode(key.replace("\n", ""));
|
||||||
|
X509EncodedKeySpec spec = new X509EncodedKeySpec(byteKey);
|
||||||
|
KeyFactory factory = KeyFactory.getInstance("RSA");
|
||||||
|
return factory.generatePublic(spec);
|
||||||
|
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(final GameProfile profile, final boolean requireSecure) {
|
||||||
|
final Property textureProperty = Iterables.getFirst(profile.getProperties().get("textures"), null);
|
||||||
|
|
||||||
|
if (textureProperty == null)
|
||||||
|
return new HashMap<>();
|
||||||
|
|
||||||
|
if (requireSecure) {
|
||||||
|
if (!textureProperty.hasSignature()) {
|
||||||
|
LOGGER.error("Signature is missing from textures payload");
|
||||||
|
throw new InsecureTextureException("Signature is missing from textures payload");
|
||||||
|
}
|
||||||
|
if (!textureProperty.isSignatureValid(publicKey)) {
|
||||||
|
LOGGER.error("Textures payload has been tampered with (signature invalid)");
|
||||||
|
throw new InsecureTextureException("Textures payload has been tampered with (signature invalid)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final MinecraftTexturesPayload result;
|
||||||
|
try {
|
||||||
|
final String json = new String(org.apache.commons.codec.binary.Base64.decodeBase64(textureProperty.getValue()), StandardCharsets.UTF_8);
|
||||||
|
result = gson.fromJson(json, MinecraftTexturesPayload.class);
|
||||||
|
} catch (final JsonParseException e) {
|
||||||
|
LOGGER.error("Could not decode textures payload", e);
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == null || result.getTextures() == null)
|
||||||
|
return new HashMap<>();
|
||||||
|
|
||||||
|
return result.getTextures();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LocalYggdrasilAuthenticationService extends YggdrasilAuthenticationService {
|
||||||
|
public final String server;
|
||||||
|
|
||||||
|
public LocalYggdrasilAuthenticationService(Proxy proxy, String server) {
|
||||||
|
super(proxy, new LocalYggdrasilApi(server));
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,18 +3,22 @@
|
|||||||
"package": "anticope.rejects.mixin.meteor",
|
"package": "anticope.rejects.mixin.meteor",
|
||||||
"compatibilityLevel": "JAVA_16",
|
"compatibilityLevel": "JAVA_16",
|
||||||
"client": [
|
"client": [
|
||||||
|
"AccountMixin",
|
||||||
|
"AccountsMixin",
|
||||||
|
"AccountsScreenMixin",
|
||||||
"ConfigTabMixin",
|
"ConfigTabMixin",
|
||||||
|
"DefaultSettingsWidgetFactoryMixin",
|
||||||
|
"FontUtilsMixin",
|
||||||
"GuiRendererAccessor",
|
"GuiRendererAccessor",
|
||||||
"HttpMixin",
|
"HttpMixin",
|
||||||
"ModuleMixin",
|
"ModuleMixin",
|
||||||
"ModulesMixin",
|
"ModulesMixin",
|
||||||
"FontUtilsMixin",
|
"WAccountMixin",
|
||||||
"DefaultSettingsWidgetFactoryMixin",
|
"modules.AimAssistMixin",
|
||||||
"modules.FlightMixin",
|
|
||||||
"modules.NoRenderAccessor",
|
|
||||||
"modules.AutoSignMixin",
|
"modules.AutoSignMixin",
|
||||||
|
"modules.FlightMixin",
|
||||||
"modules.InventoryTweaksMixin",
|
"modules.InventoryTweaksMixin",
|
||||||
"modules.KillAuraMixin",
|
"modules.KillAuraMixin",
|
||||||
"modules.AimAssistMixin"
|
"modules.NoRenderAccessor"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,11 @@
|
|||||||
"GameRendererMixin",
|
"GameRendererMixin",
|
||||||
"LivingEntityMixin",
|
"LivingEntityMixin",
|
||||||
"LivingEntityRendererMixin",
|
"LivingEntityRendererMixin",
|
||||||
|
"MinecraftClientAccessor",
|
||||||
"MultiplayerScreenAccessor",
|
"MultiplayerScreenAccessor",
|
||||||
"MultiplayerScreenMixin",
|
"MultiplayerScreenMixin",
|
||||||
"PlayerMoveC2SPacketAccessor",
|
"PlayerMoveC2SPacketAccessor",
|
||||||
|
"PlayerSkinProviderAccessor",
|
||||||
"ServerListAccessor",
|
"ServerListAccessor",
|
||||||
"StructureVoidBlockMixin",
|
"StructureVoidBlockMixin",
|
||||||
"TexturedRenderLayersMixin",
|
"TexturedRenderLayersMixin",
|
||||||
|
|||||||
Reference in New Issue
Block a user