From 48af8ec95d4a83d7c0fe43ba7416875e2299613a Mon Sep 17 00:00:00 2001 From: Luca Date: Wed, 15 Jan 2025 18:26:15 +0100 Subject: [PATCH 1/4] Improvements to 2FA system: [+] Add 2fa without protocolize (fallback) [+] Add 2fadisable command --- .../authorization/AuthorizationProvider.java | 18 ++++++++ .../common/AuthenticLibreLogin.java | 3 +- .../AuthenticAuthorizationProvider.java | 23 ++++++++++ .../common/command/CommandProvider.java | 2 + .../commands/tfa/TwoFactorAuthCommand.java | 21 ++++----- .../tfa/TwoFactorAuthDisableCommand.java | 46 +++++++++++++++++++ .../librelogin/common/config/MessageKeys.java | 46 ++++++++++++++++++- 7 files changed, 145 insertions(+), 14 deletions(-) create mode 100644 Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/tfa/TwoFactorAuthDisableCommand.java diff --git a/API/src/main/java/xyz/kyngs/librelogin/api/authorization/AuthorizationProvider.java b/API/src/main/java/xyz/kyngs/librelogin/api/authorization/AuthorizationProvider.java index 0d36304d..3a582791 100644 --- a/API/src/main/java/xyz/kyngs/librelogin/api/authorization/AuthorizationProvider.java +++ b/API/src/main/java/xyz/kyngs/librelogin/api/authorization/AuthorizationProvider.java @@ -33,6 +33,14 @@ public interface AuthorizationProvider

{ */ boolean isAwaiting2FA(P player); + /** + * Checks whether the player is in the process of disabling 2FA. + * + * @param player The player. + * @return True if the player needs to confirm the action of disabling 2FA, false otherwise. + */ + boolean isAwaiting2FADisable(P player); + /** * Authorizes the player, if the player is not already authorized. Implementation must make sure that {@link #isAuthorized(P)} returns false. * @@ -51,4 +59,14 @@ public interface AuthorizationProvider

{ * @return whether the code is valid. */ boolean confirmTwoFactorAuth(P player, Integer code, User user); + + /** + * Finishes the 2FA disabling process. + * + * @param player The player. + * @param code The code. + * @param user The user. + * @return whether the code is valid. + */ + boolean confirmTwoFactorAuthDisable(P player, Integer code, User user); } diff --git a/Plugin/src/main/java/xyz/kyngs/librelogin/common/AuthenticLibreLogin.java b/Plugin/src/main/java/xyz/kyngs/librelogin/common/AuthenticLibreLogin.java index 5c873072..4616b986 100644 --- a/Plugin/src/main/java/xyz/kyngs/librelogin/common/AuthenticLibreLogin.java +++ b/Plugin/src/main/java/xyz/kyngs/librelogin/common/AuthenticLibreLogin.java @@ -302,13 +302,12 @@ protected void enable() { if (imageProjector != null) { if (!configuration.get(TOTP_ENABLED)) { imageProjector = null; - logger.warn("2FA is disabled in the configuration, aborting..."); } else { imageProjector.enable(); } } - totpProvider = imageProjector == null ? null : new AuthenticTOTPProvider(this); + totpProvider = configuration.get(TOTP_ENABLED) ? new AuthenticTOTPProvider(this) : null; eMailHandler = configuration.get(MAIL_ENABLED) ? new AuthenticEMailHandler(this) : null; authorizationProvider = new AuthenticAuthorizationProvider<>(this); diff --git a/Plugin/src/main/java/xyz/kyngs/librelogin/common/authorization/AuthenticAuthorizationProvider.java b/Plugin/src/main/java/xyz/kyngs/librelogin/common/authorization/AuthenticAuthorizationProvider.java index 51e2f5f6..7b8c2c6a 100644 --- a/Plugin/src/main/java/xyz/kyngs/librelogin/common/authorization/AuthenticAuthorizationProvider.java +++ b/Plugin/src/main/java/xyz/kyngs/librelogin/common/authorization/AuthenticAuthorizationProvider.java @@ -33,6 +33,7 @@ public class AuthenticAuthorizationProvider extends AuthenticHandler private final Map unAuthorized; private final Map awaiting2FA; + private final Map awaiting2FADisable; private final Cache emailConfirmCache; private final Cache passwordResetCache; @@ -40,6 +41,7 @@ public AuthenticAuthorizationProvider(AuthenticLibreLogin plugin) { super(plugin); unAuthorized = new ConcurrentHashMap<>(); awaiting2FA = new ConcurrentHashMap<>(); + awaiting2FADisable = new ConcurrentHashMap<>(); var millis = plugin.getConfiguration().get(ConfigurationKeys.MILLISECONDS_TO_REFRESH_NOTIFICATION); @@ -69,6 +71,7 @@ public Cache getPasswordResetCache() { public void onExit(P player) { stopTracking(player); awaiting2FA.remove(player); + awaiting2FADisable.remove(player); emailConfirmCache.invalidate(platformHandle.getUUIDForPlayer(player)); passwordResetCache.invalidate(platformHandle.getUUIDForPlayer(player)); } @@ -83,6 +86,11 @@ public boolean isAwaiting2FA(P player) { return awaiting2FA.containsKey(player); } + @Override + public boolean isAwaiting2FADisable(P player) { + return awaiting2FADisable.containsKey(player); + } + @Override public void authorize(User user, P player, AuthenticatedEvent.AuthenticationReason reason) { if (isAuthorized(player)) { @@ -113,6 +121,17 @@ public boolean confirmTwoFactorAuth(P player, Integer code, User user) { return false; } + @Override + public boolean confirmTwoFactorAuthDisable(P player, Integer code, User user) { + var secret = awaiting2FADisable.get(player); + if (plugin.getTOTPProvider().verify(code, secret)) { + user.setSecret(null); + plugin.getDatabaseProvider().updateUser(user); + return true; + } + return false; + } + public void startTracking(User user, P player) { var audience = platformHandle.getAudienceForPlayer(player); @@ -215,4 +234,8 @@ public void beginTwoFactorAuth(User user, P player, TOTPData data) { if (t != null || e != null) awaiting2FA.remove(player); }); } + + public void beginTwoFactorAuthDisable(User user, P player) { + awaiting2FADisable.put(player, user.getSecret()); + } } diff --git a/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/CommandProvider.java b/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/CommandProvider.java index b6ac659c..25ed3a9f 100644 --- a/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/CommandProvider.java +++ b/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/CommandProvider.java @@ -28,6 +28,7 @@ import xyz.kyngs.librelogin.common.command.commands.premium.PremiumEnableCommand; import xyz.kyngs.librelogin.common.command.commands.staff.LibreLoginCommand; import xyz.kyngs.librelogin.common.command.commands.tfa.TwoFactorAuthCommand; +import xyz.kyngs.librelogin.common.command.commands.tfa.TwoFactorAuthDisableCommand; import xyz.kyngs.librelogin.common.command.commands.tfa.TwoFactorConfirmCommand; import xyz.kyngs.librelogin.common.util.RateLimiter; @@ -110,6 +111,7 @@ public CommandProvider(AuthenticLibreLogin plugin) { if (plugin.getTOTPProvider() != null) { manager.registerCommand(new TwoFactorAuthCommand<>(plugin)); manager.registerCommand(new TwoFactorConfirmCommand<>(plugin)); + manager.registerCommand(new TwoFactorAuthDisableCommand<>(plugin)); } if (plugin.getEmailHandler() != null) { diff --git a/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/tfa/TwoFactorAuthCommand.java b/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/tfa/TwoFactorAuthCommand.java index 1f4a651c..123b0271 100644 --- a/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/tfa/TwoFactorAuthCommand.java +++ b/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/tfa/TwoFactorAuthCommand.java @@ -33,24 +33,23 @@ public CompletionStage onTwoFactorAuth(Audience sender, P player) { throw new InvalidCommandArgument(getMessage("totp-show-info")); } - if (!plugin.getImageProjector().canProject(player)) { - throw new InvalidCommandArgument(getMessage("totp-wrong-version", - "%low%", "1.13", - "%high%", "1.21.1" - )); - } - sender.sendMessage(getMessage("totp-generating")); var data = plugin.getTOTPProvider().generate(user); auth.beginTwoFactorAuth(user, player, data); - plugin.cancelOnExit(plugin.delay(() -> { - plugin.getImageProjector().project(data.qr(), player); + if (plugin.getImageProjector() != null && plugin.getImageProjector().canProject(player)) { + plugin.cancelOnExit(plugin.delay(() -> { + plugin.getImageProjector().project(data.qr(), player); - sender.sendMessage(getMessage("totp-show-info")); - }, plugin.getConfiguration().get(ConfigurationKeys.TOTP_DELAY)), player); + sender.sendMessage(getMessage("totp-show-info")); + }, plugin.getConfiguration().get(ConfigurationKeys.TOTP_DELAY)), player); + } else { + sender.sendMessage(getMessage("totp-show-info-fallback", + "%totp_secret%", data.secret() + )); + } }); } } diff --git a/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/tfa/TwoFactorAuthDisableCommand.java b/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/tfa/TwoFactorAuthDisableCommand.java new file mode 100644 index 00000000..712e1f4d --- /dev/null +++ b/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/tfa/TwoFactorAuthDisableCommand.java @@ -0,0 +1,46 @@ +package xyz.kyngs.librelogin.common.command.commands.tfa; + +import co.aikar.commands.annotation.*; +import net.kyori.adventure.audience.Audience; +import xyz.kyngs.librelogin.common.AuthenticLibreLogin; +import xyz.kyngs.librelogin.common.command.Command; +import xyz.kyngs.librelogin.common.command.InvalidCommandArgument; + +import java.util.concurrent.CompletionStage; + +@CommandAlias("2fadisable") +public class TwoFactorAuthDisableCommand

extends Command

{ + public TwoFactorAuthDisableCommand(AuthenticLibreLogin plugin) { + super(plugin); + } + + @Default + @Syntax("{@@syntax.2fa-disable}") + @CommandCompletion("%autocomplete.2fa-disable") + public CompletionStage onTwoFactorAuthDisable(Audience sender, P player, @Optional String code) { + return runAsync(() -> { + checkAuthorized(player); + var user = getUser(player); + var auth = plugin.getAuthorizationProvider(); + int parsedCode; + + if (auth.isAwaiting2FADisable(player) && code != null) { + try { + parsedCode = Integer.parseInt(code.trim().replace(" ", "")); + } catch (NumberFormatException e) { + throw new InvalidCommandArgument(getMessage("totp-wrong")); + } + + if (!auth.confirmTwoFactorAuthDisable(player, parsedCode, user)) { + throw new InvalidCommandArgument(getMessage("totp-wrong")); + } + + sender.sendMessage(getMessage("totp-disable-confirm")); + }else{ + auth.beginTwoFactorAuthDisable(user, player); + + sender.sendMessage(getMessage("totp-disable")); + } + }); + } +} diff --git a/Plugin/src/main/java/xyz/kyngs/librelogin/common/config/MessageKeys.java b/Plugin/src/main/java/xyz/kyngs/librelogin/common/config/MessageKeys.java index 5d47863f..14e05aae 100644 --- a/Plugin/src/main/java/xyz/kyngs/librelogin/common/config/MessageKeys.java +++ b/Plugin/src/main/java/xyz/kyngs/librelogin/common/config/MessageKeys.java @@ -778,6 +778,36 @@ public class MessageKeys { ConfigurateHelper::getString ); + public static final ConfigurationKey TOTP_SHOW_INFO_FALLBACK = new ConfigurationKey<>( + "totp-show-info-fallback", + """ + It is currently not possible to display the 2FA QR code on screen. + Please open your 2FA app (e.g., Google Authenticator or Authy) and manually add a new entry using the following secret key: + %totp_secret% + Once done, execute the /2faconfirm command to complete the process. + Disconnect to abort.""", + "This message is displayed when the player is prompted to scan the 2FA QR code but can't show in game graphic.", + ConfigurateHelper::getString + ); + + public static final ConfigurationKey TOTP_DISABLE = new ConfigurationKey<>( + "totp-disable", + """ + Please confirm that you want to disable 2FA by executing the command again: + /2fadisable + Once 2FA is disabled, you will be able to log in without a 2FA code. + This action is irreversible. Once disabled, old codes will no longer work.""", + "This message is displayed when the player is prompted to confirm he wants to disable 2fa.", + ConfigurateHelper::getString + ); + + public static final ConfigurationKey TOTP_DISABLE_CONFIRM = new ConfigurationKey<>( + "totp-disable-confirm", + "2FA has been disabled! You can now log in without a 2FA code.", + "This message is displayed when the 2fa is disabled.", + ConfigurateHelper::getString + ); + public static final ConfigurationKey TOTP_GENERATING = new ConfigurationKey<>( "totp-generating", "Generating 2FA code...", @@ -964,6 +994,13 @@ public class MessageKeys { ConfigurateHelper::getString ); + public static final ConfigurationKey SYNTAX_2FA_DISABLE = new ConfigurationKey<>( + "syntax.2fa-disable", + "", + "This message is displayed when the player attempts to disable 2FA with wrong syntax.", + ConfigurateHelper::getString + ); + public static final ConfigurationKey SYNTAX_CHANGE_PASSWORD = new ConfigurationKey<>( "syntax.change-password", " ", @@ -1133,7 +1170,14 @@ public class MessageKeys { public static final ConfigurationKey AUTOCOMPLETE_2FA_CONFIRM = new ConfigurationKey<>( "autocomplete.2fa-confirm", "code", - "This hint is displayed when the player starts typing the /2fa-confirm command.", + "This hint is displayed when the player starts typing the /2faconfirm command.", + ConfigurateHelper::getString + ); + + public static final ConfigurationKey AUTOCOMPLETE_2FA_DISABLE = new ConfigurationKey<>( + "autocomplete.2fa-disable", + "code", + "This hint is displayed when the player starts typing the /2fadisable command.", ConfigurateHelper::getString ); From 3e49f3af2c316d51720b2fa8414f8d21384c5ddb Mon Sep 17 00:00:00 2001 From: Luca Date: Sat, 29 Mar 2025 13:28:22 +0100 Subject: [PATCH 2/4] Add missing license on new file --- .../command/commands/tfa/TwoFactorAuthDisableCommand.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/tfa/TwoFactorAuthDisableCommand.java b/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/tfa/TwoFactorAuthDisableCommand.java index 712e1f4d..3a06abfd 100644 --- a/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/tfa/TwoFactorAuthDisableCommand.java +++ b/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/tfa/TwoFactorAuthDisableCommand.java @@ -1,3 +1,9 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + package xyz.kyngs.librelogin.common.command.commands.tfa; import co.aikar.commands.annotation.*; From c1f051c6c6d0ee13a413ffa542a14572ca866068 Mon Sep 17 00:00:00 2001 From: Luca Date: Tue, 8 Apr 2025 00:09:47 +0200 Subject: [PATCH 3/4] fix: Reloading the configuration now reloads also limbo and lobby without restart of the server. --- .../librelogin/api/LibreLoginPlugin.java | 7 ++++ .../librelogin/api/server/ServerHandler.java | 5 +++ .../common/AuthenticLibreLogin.java | 39 ++++++++++++------- .../commands/staff/LibreLoginCommand.java | 2 +- .../common/server/AuthenticServerHandler.java | 11 +++++- 5 files changed, 48 insertions(+), 16 deletions(-) diff --git a/API/src/main/java/xyz/kyngs/librelogin/api/LibreLoginPlugin.java b/API/src/main/java/xyz/kyngs/librelogin/api/LibreLoginPlugin.java index ce96eae9..459f0bb8 100644 --- a/API/src/main/java/xyz/kyngs/librelogin/api/LibreLoginPlugin.java +++ b/API/src/main/java/xyz/kyngs/librelogin/api/LibreLoginPlugin.java @@ -7,6 +7,7 @@ package xyz.kyngs.librelogin.api; import xyz.kyngs.librelogin.api.authorization.AuthorizationProvider; +import xyz.kyngs.librelogin.api.configuration.CorruptedConfigurationException; import xyz.kyngs.librelogin.api.configuration.Messages; import xyz.kyngs.librelogin.api.crypto.CryptoProvider; import xyz.kyngs.librelogin.api.crypto.HashedPassword; @@ -24,6 +25,7 @@ import xyz.kyngs.librelogin.api.util.ThrowableFunction; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.sql.Timestamp; import java.util.Map; @@ -291,4 +293,9 @@ User createUser( String lastServer, String email ); + + /** + * Reloads the configurations and registers/unregisters new handlers (if possible). + */ + void reloadConfiguration() throws CorruptedConfigurationException, IOException; } diff --git a/API/src/main/java/xyz/kyngs/librelogin/api/server/ServerHandler.java b/API/src/main/java/xyz/kyngs/librelogin/api/server/ServerHandler.java index 7bc363e6..9acfd108 100644 --- a/API/src/main/java/xyz/kyngs/librelogin/api/server/ServerHandler.java +++ b/API/src/main/java/xyz/kyngs/librelogin/api/server/ServerHandler.java @@ -100,4 +100,9 @@ default void registerLobbyServer(S server) { */ void registerLimboServer(S server); + /** + * Shuts down server handler. + */ + void shutdown(); + } diff --git a/Plugin/src/main/java/xyz/kyngs/librelogin/common/AuthenticLibreLogin.java b/Plugin/src/main/java/xyz/kyngs/librelogin/common/AuthenticLibreLogin.java index 4616b986..1c4d1c6a 100644 --- a/Plugin/src/main/java/xyz/kyngs/librelogin/common/AuthenticLibreLogin.java +++ b/Plugin/src/main/java/xyz/kyngs/librelogin/common/AuthenticLibreLogin.java @@ -290,25 +290,12 @@ protected void enable() { connectToDB(); - serverHandler = new AuthenticServerHandler<>(this); - this.loginTryListener = new LoginTryListener<>(this); // Moved to a different class to avoid class loading issues GeneralUtil.checkAndMigrate(configuration, logger, this); - imageProjector = provideImageProjector(); - - if (imageProjector != null) { - if (!configuration.get(TOTP_ENABLED)) { - imageProjector = null; - } else { - imageProjector.enable(); - } - } - - totpProvider = configuration.get(TOTP_ENABLED) ? new AuthenticTOTPProvider(this) : null; - eMailHandler = configuration.get(MAIL_ENABLED) ? new AuthenticEMailHandler(this) : null; + reloadComponents(); authorizationProvider = new AuthenticAuthorizationProvider<>(this); commandProvider = new CommandProvider<>(this); @@ -716,6 +703,30 @@ public void checkDataFolder() { if (!folder.exists()) if (!folder.mkdir()) throw new RuntimeException("Failed to create datafolder"); } + @Override + public void reloadConfiguration() throws CorruptedConfigurationException, IOException { + this.getConfiguration().reload(this); + if (serverHandler != null) serverHandler.shutdown(); + reloadComponents(); + } + + private void reloadComponents() { + serverHandler = new AuthenticServerHandler<>(this); + + imageProjector = provideImageProjector(); + + if (imageProjector != null) { + if (!configuration.get(TOTP_ENABLED)) { + imageProjector = null; + } else { + imageProjector.enable(); + } + } + + totpProvider = configuration.get(TOTP_ENABLED) ? new AuthenticTOTPProvider(this) : null; + eMailHandler = configuration.get(MAIL_ENABLED) ? new AuthenticEMailHandler(this) : null; + } + protected abstract Logger provideLogger(); public abstract CommandManager provideManager(); diff --git a/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/staff/LibreLoginCommand.java b/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/staff/LibreLoginCommand.java index a7ef0683..5f7bec3f 100644 --- a/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/staff/LibreLoginCommand.java +++ b/Plugin/src/main/java/xyz/kyngs/librelogin/common/command/commands/staff/LibreLoginCommand.java @@ -160,7 +160,7 @@ public CompletionStage onReloadConfiguration(Audience audience) { audience.sendMessage(getMessage("info-reloading")); try { - plugin.getConfiguration().reload(plugin); + plugin.reloadConfiguration(); } catch (IOException e) { e.printStackTrace(); throw new InvalidCommandArgument(getMessage("error-unknown")); diff --git a/Plugin/src/main/java/xyz/kyngs/librelogin/common/server/AuthenticServerHandler.java b/Plugin/src/main/java/xyz/kyngs/librelogin/common/server/AuthenticServerHandler.java index d59bb736..42a3ffb1 100644 --- a/Plugin/src/main/java/xyz/kyngs/librelogin/common/server/AuthenticServerHandler.java +++ b/Plugin/src/main/java/xyz/kyngs/librelogin/common/server/AuthenticServerHandler.java @@ -19,6 +19,7 @@ import xyz.kyngs.librelogin.common.config.ConfigurationKeys; import xyz.kyngs.librelogin.common.event.events.AuthenticLimboServerChooseEvent; import xyz.kyngs.librelogin.common.event.events.AuthenticLobbyServerChooseEvent; +import xyz.kyngs.librelogin.common.util.CancellableTask; import java.util.ArrayList; import java.util.Collection; @@ -33,6 +34,7 @@ public class AuthenticServerHandler implements ServerHandler { private final AuthenticLibreLogin plugin; private final Collection limboServers; private final Multimap lobbyServers; + private final CancellableTask pingCacheRefreshTask; public AuthenticServerHandler(AuthenticLibreLogin plugin) { this.plugin = plugin; @@ -52,7 +54,7 @@ public AuthenticServerHandler(AuthenticLibreLogin plugin) { return Optional.ofNullable(plugin.getConfiguration().get(IGNORE_MAX_PLAYERS_FROM_BACKEND_PING) ? new ServerPing(Integer.MAX_VALUE) : ping); }); - plugin.repeat(() -> pingCache.refreshAll(pingCache.asMap().keySet()), 10000, 10000); + pingCacheRefreshTask = plugin.repeat(() -> pingCache.refreshAll(pingCache.asMap().keySet()), 10000, 10000); var handle = plugin.getPlatformHandle(); @@ -182,4 +184,11 @@ public void registerLimboServer(S server) { getLatestPing(server); limboServers.add(server); } + + @Override + public void shutdown() { + pingCacheRefreshTask.cancel(); + pingCache.invalidateAll(); + pingCache.cleanUp(); + } } From 3c8449cf5780a4c1536f690d0e6c3b14fa5e8c90 Mon Sep 17 00:00:00 2001 From: Luca Date: Sat, 23 Aug 2025 10:15:33 +0200 Subject: [PATCH 4/4] Fix: If fallback is disabled, do not disconnect the player as other plugin with compatibility might break --- .../java/xyz/kyngs/librelogin/velocity/VelocityListeners.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Plugin/src/main/java/xyz/kyngs/librelogin/velocity/VelocityListeners.java b/Plugin/src/main/java/xyz/kyngs/librelogin/velocity/VelocityListeners.java index 9c1c3e32..b0ad3519 100644 --- a/Plugin/src/main/java/xyz/kyngs/librelogin/velocity/VelocityListeners.java +++ b/Plugin/src/main/java/xyz/kyngs/librelogin/velocity/VelocityListeners.java @@ -167,9 +167,10 @@ public void onKick(KickedFromServerEvent event) { if (event.kickedDuringServerConnect()) { event.setResult(KickedFromServerEvent.Notify.create(message)); } else { - if (!plugin.getConfiguration().get(ConfigurationKeys.FALLBACK) || plugin.getServerHandler().getLobbyServers().containsValue(event.getServer())) { + if (plugin.getServerHandler().getLobbyServers().containsValue(event.getServer())) { event.setResult(KickedFromServerEvent.DisconnectPlayer.create(message)); } else { + if (!plugin.getConfiguration().get(ConfigurationKeys.FALLBACK)) return; try { var server = plugin.getServerHandler().chooseLobbyServer(plugin.getDatabaseProvider().getByUUID(player.getUniqueId()), player, false, true);