Skip to content

Commit

Permalink
feat: Device auth
Browse files Browse the repository at this point in the history
  • Loading branch information
VidTu committed Feb 9, 2024
1 parent bf91e2c commit bb51589
Show file tree
Hide file tree
Showing 33 changed files with 1,579 additions and 579 deletions.
4 changes: 2 additions & 2 deletions 1.20.4/src/main/java/ru/vidtu/ias/IASMinecraft.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
import net.minecraft.resources.ResourceLocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.vidtu.ias.account.Account;
import ru.vidtu.ias.auth.LoginData;
import ru.vidtu.ias.config.IASConfig;
import ru.vidtu.ias.mixins.MinecraftAccessor;
import ru.vidtu.ias.screen.AccountScreen;
Expand Down Expand Up @@ -297,7 +297,7 @@ public static void onDraw(Screen screen, Font font, GuiGraphics graphics) {
* @param data Login data
* @return Future for logging in
*/
public static CompletableFuture<Void> account(Minecraft minecraft, Account.LoginData data) {
public static CompletableFuture<Void> account(Minecraft minecraft, LoginData data) {
// Check if not in-game.
LOGGER.info("IAS: Received login request: {}", data);
if (minecraft.player != null || minecraft.level != null || minecraft.getConnection() != null ||
Expand Down
3 changes: 2 additions & 1 deletion 1.20.4/src/main/java/ru/vidtu/ias/screen/AccountList.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import ru.vidtu.ias.IAS;
import ru.vidtu.ias.account.Account;
import ru.vidtu.ias.account.OfflineAccount;
import ru.vidtu.ias.auth.LoginData;
import ru.vidtu.ias.config.IASStorage;

import java.util.Locale;
Expand Down Expand Up @@ -161,7 +162,7 @@ void login(boolean online) {

// Login offline.
String name = account.name();
Account.LoginData data = new Account.LoginData(name, OfflineAccount.uuid(name), "ias:offline", false);
LoginData data = new LoginData(name, OfflineAccount.uuid(name), "ias:offline", false);
login.success(data, false);
}

Expand Down
16 changes: 16 additions & 0 deletions 1.20.4/src/main/java/ru/vidtu/ias/screen/ConfigScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.slf4j.LoggerFactory;
import ru.vidtu.ias.IAS;
import ru.vidtu.ias.config.IASConfig;
import ru.vidtu.ias.config.ServerMode;
import ru.vidtu.ias.config.TextAlign;
import ru.vidtu.ias.utils.Expression;

Expand Down Expand Up @@ -367,6 +368,21 @@ protected void init() {
box.setTooltipDelay(250);
this.addRenderableWidget(box);

// Sun Server.
Button button = Button.builder(CommonComponents.optionNameValue(Component.translatable("ias.config.server"), Component.translatable(IASConfig.server.toString())), btn -> {
// Update the value.
IASConfig.server = switch (IASConfig.server) {
case ALWAYS -> ServerMode.AVAILABLE;
case AVAILABLE -> ServerMode.NEVER;
case NEVER -> ServerMode.ALWAYS;
};

// Set the message.
btn.setMessage(CommonComponents.optionNameValue(Component.translatable("ias.config.server"), Component.translatable(IASConfig.server.toString())));
}).bounds(9 + box.getWidth(), 116, 200, 20).tooltip(Tooltip.create(Component.translatable("ias.config.server.tip"))).build();
button.setTooltipDelay(250);
this.addRenderableWidget(button);

// Nick Warns.
box = Checkbox.builder(Component.translatable("ias.config.nickWarns"), this.font)
.pos(5, 140)
Expand Down
26 changes: 18 additions & 8 deletions 1.20.4/src/main/java/ru/vidtu/ias/screen/LoginPopupScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@
import org.slf4j.LoggerFactory;
import ru.vidtu.ias.IAS;
import ru.vidtu.ias.IASMinecraft;
import ru.vidtu.ias.account.Account;
import ru.vidtu.ias.account.MicrosoftAccount;
import ru.vidtu.ias.utils.ProbableException;
import ru.vidtu.ias.auth.LoginData;
import ru.vidtu.ias.auth.handlers.LoginHandler;
import ru.vidtu.ias.utils.exceptions.FriendlyException;

import java.util.Objects;
import java.util.concurrent.CompletableFuture;
Expand All @@ -45,7 +46,7 @@
*
* @author VidTu
*/
final class LoginPopupScreen extends Screen implements Account.LoginHandler {
final class LoginPopupScreen extends Screen implements LoginHandler {
/**
* Logger for this class.
*/
Expand Down Expand Up @@ -93,6 +94,15 @@ final class LoginPopupScreen extends Screen implements Account.LoginHandler {
this.parent = parent;
}

@Override
public boolean cancelled() {
// Bruh.
assert this.minecraft != null;

// Cancelled if no longer displayed.
return this != this.minecraft.screen;
}

@Override
protected void init() {
// Bruh.
Expand Down Expand Up @@ -226,15 +236,15 @@ public void renderBackground(GuiGraphics graphics, int mouseX, int mouseY, float
}

@Override
public void stage(String stage) {
public void stage(String stage, Object... args) {
// Bruh.
assert this.minecraft != null;

// Skip if not current screen.
if (this != this.minecraft.screen) return;

// Flush the stage.
Component component = Component.translatable(stage).withStyle(ChatFormatting.YELLOW);
Component component = Component.translatable(stage, args).withStyle(ChatFormatting.YELLOW);
synchronized (this.lock) {
this.stage = component;
this.label = null;
Expand Down Expand Up @@ -270,7 +280,7 @@ public CompletableFuture<String> password() {
}

@Override
public void success(Account.LoginData data, boolean changed) {
public void success(LoginData data, boolean changed) {
// Bruh.
assert this.minecraft != null;

Expand Down Expand Up @@ -332,8 +342,8 @@ public void error(Throwable error) {
if (this != this.minecraft.screen) return;

// Flush the stage.
ProbableException probable = ProbableException.probableCause(error);
String key = probable != null ? Objects.requireNonNullElse(probable.getMessage(), "ias.error") : "ias.error";
FriendlyException probable = FriendlyException.friendlyInChain(error);
String key = probable != null ? probable.key() : "ias.error";
Component component = Component.translatable(key).withStyle(ChatFormatting.RED);
synchronized (this.lock) {
this.stage = component;
Expand Down
97 changes: 83 additions & 14 deletions 1.20.4/src/main/java/ru/vidtu/ias/screen/MicrosoftPopupScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@
import ru.vidtu.ias.IAS;
import ru.vidtu.ias.account.Account;
import ru.vidtu.ias.account.MicrosoftAccount;
import ru.vidtu.ias.auth.MSAuthServer;
import ru.vidtu.ias.auth.handlers.CreateHandler;
import ru.vidtu.ias.auth.microsoft.MSAuthClient;
import ru.vidtu.ias.auth.microsoft.MSAuthServer;
import ru.vidtu.ias.config.IASConfig;
import ru.vidtu.ias.crypt.Crypt;
import ru.vidtu.ias.crypt.PasswordCrypt;
import ru.vidtu.ias.utils.ProbableException;
import ru.vidtu.ias.utils.exceptions.FriendlyException;

import java.util.Locale;
import java.util.Objects;
Expand All @@ -54,7 +57,7 @@
*
* @author VidTu
*/
final class MicrosoftPopupScreen extends Screen implements MSAuthServer.CreateHandler {
final class MicrosoftPopupScreen extends Screen implements CreateHandler {
/**
* Logger for this class.
*/
Expand All @@ -80,6 +83,11 @@ final class MicrosoftPopupScreen extends Screen implements MSAuthServer.CreateHa
*/
private Crypt crypt;

/**
* MS auth client.
*/
private MSAuthClient client;

/**
* MS auth server.
*/
Expand All @@ -89,7 +97,7 @@ final class MicrosoftPopupScreen extends Screen implements MSAuthServer.CreateHa
* Current stage.
*/
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") // <- toString()
private Component stage = Component.translatable(MicrosoftAccount.INITIALIZING);
private Component stage = Component.translatable(MicrosoftAccount.INITIALIZING).withStyle(ChatFormatting.YELLOW);

/**
* Current stage label.
Expand All @@ -116,6 +124,15 @@ final class MicrosoftPopupScreen extends Screen implements MSAuthServer.CreateHa
this.crypt = crypt;
}

@Override
public boolean cancelled() {
// Bruh.
assert this.minecraft != null;

// Cancelled if no longer displayed.
return this != this.minecraft.screen;
}

@Override
protected void init() {
// Bruh.
Expand Down Expand Up @@ -171,19 +188,61 @@ protected void init() {
}

// Try to open the server.
this.server();
IAS.executor().execute(() -> {
if (IASConfig.useServerAuth()) {
this.server();
} else {
this.client();
}
});
}

/**
* Creates the client.
*/
private void client() {
try {
// Bruh.
assert this.minecraft != null;

// Skip if can't.
if (this.crypt == null || this.server != null || this.client != null) return;

// Create the server.
this.client = new MSAuthClient(this.crypt, this);

// Run the client.
this.client.start().thenAcceptAsync(auth -> {
// Log it and display progress.
LOGGER.info("IAS: Opening client link...");
this.stage(MicrosoftAccount.CLIENT_BROWSER,
Component.literal(auth.uri().toString()).withStyle(ChatFormatting.GOLD),
Component.literal(auth.user()).withStyle(ChatFormatting.GOLD));

// Copy and open link.
Util.getPlatform().openUri(auth.uri().toString());
this.minecraft.keyboardHandler.setClipboard(auth.user());
}, this.minecraft).exceptionally(t -> {
// Handle error.
this.error(new RuntimeException("Unable to handle client.", t));
return null;
});
} catch (Throwable t) {
// Handle error.
this.error(new RuntimeException("Unable to create client.", t));
}
}

/**
* Create the server.
* Creates the server.
*/
private void server() {
try {
// Bruh.
assert this.minecraft != null;

// Skip if can't.
if (this.crypt == null || this.server != null) return;
if (this.crypt == null || this.server != null || this.client != null) return;

// Create the server.
this.server = new MSAuthServer(I18n.get("ias.login.done"), this.crypt, this);
Expand All @@ -194,7 +253,7 @@ private void server() {
this.server.run();
}, IAS.executor()).thenRunAsync(() -> {
// Log it and display progress.
LOGGER.info("IAS: Opening link...");
LOGGER.info("IAS: Opening server link...");
this.stage(MicrosoftAccount.BROWSER);

// Copy and open link.
Expand Down Expand Up @@ -235,10 +294,18 @@ public void removed() {
keyboard.setClipboard(" ");
}

// Close the server, if any.
// Close off-thread.
IAS.executor().execute(() -> {
// Close the client, if any.
if (this.client != null) {
this.client.close();
this.client = null;
}

// Close the server, if any.
if (this.server != null) {
this.server.close();
this.server = null;
}
});
}
Expand Down Expand Up @@ -316,7 +383,7 @@ public void renderBackground(GuiGraphics graphics, int mouseX, int mouseY, float
}

@Override
public void stage(String stage) {
public void stage(String stage, Object... args) {
// Bruh.
assert this.minecraft != null;

Expand All @@ -334,7 +401,7 @@ public void stage(String stage) {
}

// Flush the stage.
Component component = Component.translatable(stage).withStyle(ChatFormatting.YELLOW);
Component component = Component.translatable(stage, args).withStyle(ChatFormatting.YELLOW);
synchronized (this.lock) {
this.stage = component;
this.label = null;
Expand Down Expand Up @@ -374,8 +441,8 @@ public void error(Throwable error) {
if (this != this.minecraft.screen) return;

// Flush the stage.
ProbableException probable = ProbableException.probableCause(error);
String key = probable != null ? Objects.requireNonNullElse(probable.getMessage(), "ias.error") : "ias.error";
FriendlyException probable = FriendlyException.friendlyInChain(error);
String key = probable != null ? probable.key() : "ias.error";
Component component = Component.translatable(key).withStyle(ChatFormatting.RED);
synchronized (this.lock) {
this.stage = component;
Expand All @@ -386,7 +453,9 @@ public void error(Throwable error) {
@Override
public String toString() {
return "MicrosoftPopupScreen{" +
", crypt=" + this.crypt +
"crypt=" + this.crypt +
", client=" + this.client +
", server=" + this.server +
", stage=" + this.stage +
", label=" + this.label +
'}';
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,8 @@ critical enough not to be disclosed publicly, please, report it as described in

![image](https://i.imgur.com/DX06VoG.png)
![image](https://i.imgur.com/5hiQ6Om.png)

## Credits

Thanks to the [wiki.vg/Microsoft_Authentication_Scheme](https://wiki.vg/Microsoft_Authentication_Scheme)
page for providing useful information about Microsoft authentication.
14 changes: 10 additions & 4 deletions src/main/java/ru/vidtu/ias/IAS.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.vidtu.ias.auth.MSAuth;
import ru.vidtu.ias.auth.microsoft.MSAuth;
import ru.vidtu.ias.config.IASConfig;
import ru.vidtu.ias.config.IASStorage;

Expand Down Expand Up @@ -49,6 +49,11 @@ public final class IAS {
*/
public static final String CLIENT_ID = "54fd49e4-2103-4044-9603-2b028c814ec3";

/**
* Request timeout.
*/
public static final Duration TIMEOUT = Duration.ofSeconds(Long.getLong("ias.timeout", 15L));

/**
* Logger for this class.
*/
Expand Down Expand Up @@ -149,17 +154,18 @@ public static void init(Path gamePath, Path configPath, String version, String l
try {
// Create the client.
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10L))
.connectTimeout(TIMEOUT)
.version(HttpClient.Version.HTTP_2)
.followRedirects(HttpClient.Redirect.NORMAL)
.executor(Runnable::run)
.priority(1)
.build();

// Send the request.
HttpResponse<Stream<String>> response = client.send(HttpRequest.newBuilder(new URI("https://raw.githubusercontent.com/The-Fireplace-Minecraft-Mods/In-Game-Account-Switcher/main/.ias/disabled_v1"))
HttpResponse<Stream<String>> response = client.send(HttpRequest.newBuilder()
.uri(new URI("https://raw.githubusercontent.com/The-Fireplace-Minecraft-Mods/In-Game-Account-Switcher/main/.ias/disabled_v1"))
.header("User-Agent", userAgent)
.timeout(Duration.ofSeconds(10L))
.timeout(TIMEOUT)
.GET()
.build(), HttpResponse.BodyHandlers.ofLines());

Expand Down
Loading

0 comments on commit bb51589

Please sign in to comment.