diff --git a/.gitignore b/.gitignore
index 85f8a6e9e..401002e1d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -239,8 +239,9 @@ nbdist/
run/
config.yml
logs/
-public-key.pem
+key.pem
locales/
/cache/
/packs/
-/dump.json
\ No newline at end of file
+/dump.json
+/saved-refresh-tokens.json
\ No newline at end of file
diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java
index 43ab4b3fe..7c49585d5 100644
--- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java
+++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java
@@ -275,6 +275,12 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
return Paths.get(System.getProperty("user.dir"));
}
+ @Override
+ public Path getSavedUserLoginsFolder() {
+ // Return the location of the config
+ return new File(configFilename).getAbsoluteFile().getParentFile().toPath();
+ }
+
@Override
public BootstrapDumpInfo getDumpInfo() {
return new GeyserStandaloneDumpInfo(this);
diff --git a/core/pom.xml b/core/pom.xml
index 5b509e417..51fc149e3 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -149,7 +149,7 @@
com.github.GeyserMC
MCAuthLib
- 6c99331
+ d9d773e
compile
diff --git a/core/src/main/java/org/geysermc/geyser/Constants.java b/core/src/main/java/org/geysermc/geyser/Constants.java
index 49f8fa676..23fb76d16 100644
--- a/core/src/main/java/org/geysermc/geyser/Constants.java
+++ b/core/src/main/java/org/geysermc/geyser/Constants.java
@@ -37,6 +37,8 @@ public final class Constants {
public static final String FLOODGATE_DOWNLOAD_LOCATION = "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/";
+ static final String SAVED_REFRESH_TOKEN_FILE = "saved-refresh-tokens.json";
+
static {
URI wsUri = null;
try {
diff --git a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java
index 54ca36787..d40060310 100644
--- a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java
+++ b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java
@@ -97,6 +97,13 @@ public interface GeyserBootstrap {
*/
Path getConfigFolder();
+ /**
+ * @return the folder where user tokens are saved. This should always point to the location of the config.
+ */
+ default Path getSavedUserLoginsFolder() {
+ return getConfigFolder();
+ }
+
/**
* Information used for the bootstrap section of the debug dump
*
diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java
index 2577e6af1..f3ebfa4a3 100644
--- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java
+++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java
@@ -26,6 +26,7 @@
package org.geysermc.geyser;
import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.steveice10.packetlib.tcp.TcpSession;
@@ -37,6 +38,7 @@ import io.netty.channel.kqueue.KQueue;
import io.netty.util.NettyRuntime;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.internal.SystemPropertyUtil;
+import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import org.checkerframework.checker.nullness.qual.NonNull;
@@ -72,6 +74,9 @@ import org.geysermc.geyser.util.*;
import javax.naming.directory.Attribute;
import javax.naming.directory.InitialDirContext;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -79,6 +84,7 @@ import java.net.UnknownHostException;
import java.security.Key;
import java.text.DecimalFormat;
import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.regex.Matcher;
@@ -127,6 +133,8 @@ public class GeyserImpl implements GeyserApi {
private Metrics metrics;
private PendingMicrosoftAuthentication pendingMicrosoftAuthentication;
+ @Getter(AccessLevel.NONE)
+ private Map savedRefreshTokens;
private static GeyserImpl instance;
@@ -325,7 +333,7 @@ public class GeyserImpl implements GeyserApi {
metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger(""));
metrics.addCustomChart(new Metrics.SingleLineChart("players", sessionManager::size));
// Prevent unwanted words best we can
- metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().getAuthType().toString().toLowerCase()));
+ metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().getAuthType().toString().toLowerCase(Locale.ROOT)));
metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName));
metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", GeyserLocale::getDefaultLocale));
metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserImpl.VERSION));
@@ -409,6 +417,47 @@ public class GeyserImpl implements GeyserApi {
metrics = null;
}
+ if (config.getRemote().getAuthType() == AuthType.ONLINE) {
+ if (config.getUserAuths() != null && !config.getUserAuths().isEmpty()) {
+ getLogger().warning("The 'userAuths' config section is now deprecated, and will be removed in the near future! " +
+ "Please migrate to the new 'saved-user-logins' config option: " +
+ "https://wiki.geysermc.org/geyser/understanding-the-config/");
+ }
+
+ // May be written/read to on multiple threads from each GeyserSession as well as writing the config
+ savedRefreshTokens = new ConcurrentHashMap<>();
+
+ File tokensFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_REFRESH_TOKEN_FILE).toFile();
+ if (tokensFile.exists()) {
+ TypeReference