Skip to content
This repository was archived by the owner on May 5, 2026. It is now read-only.

Commit ffeda2d

Browse files
committed
fix(proxy): Correctly perform autoLogin, even if the user is online
1 parent 4d630a1 commit ffeda2d

4 files changed

Lines changed: 102 additions & 2 deletions

File tree

authme-core/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,10 @@ public void processJoin(Player player) {
176176
// Run commands
177177
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player,
178178
() -> commandManager.runCommandsOnSessionLogin(player));
179-
bukkitService.runTaskOptionallyAsync(() -> asynchronousLogin.forceLogin(player));
179+
// Use forceLoginFromProxy (quiet=true, no BungeeCord redirect) so that if
180+
// BungeeReceiver.performLogin() concurrently already completed the login, this
181+
// call is a no-op rather than sending an "already logged in" error.
182+
bukkitService.runTaskOptionallyAsync(() -> asynchronousLogin.forceLoginFromProxy(player));
180183
logger.info("The user " + player.getName() + " has been automatically logged in, "
181184
+ "as present in autologin queue.");
182185
return;

authme-core/src/main/java/fr/xephi/authme/service/bungeecord/BungeeReceiver.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,19 @@ private boolean verifyHmac(String playerName, long timestamp, String providedHma
129129

130130
private void performLogin(String name) {
131131
logger.debug("Received perform.login request for " + name);
132+
// Always queue in the proxy session manager so processJoin can consume it even when
133+
// the player is already online (PlayerJoinEvent fires before ServerSwitchEvent on the
134+
// proxy, so processJoin may run before perform.login arrives at this backend).
135+
proxySessionManager.processProxySessionMessage(name);
132136
Player player = bukkitService.getPlayerExact(name);
133137
if (player != null && player.isOnline()) {
138+
// Player is already online: also drive the login directly in case processJoin
139+
// has already run past the proxy-session check and created a limbo player.
134140
management.forceLoginFromProxy(player);
135141
logger.debug("Sending auto-login ACK for " + player.getName());
136142
bungeeSender.sendAuthMeBungeecordMessage(player, MessageType.PERFORM_LOGIN_ACK);
137143
logger.info(player.getName() + " has been automatically logged in via proxy request.");
138144
} else {
139-
proxySessionManager.processProxySessionMessage(name);
140145
logger.info(name + " is not yet online; queued for auto-login when they connect.");
141146
}
142147
}

authme-core/src/test/java/fr/xephi/authme/process/join/AsynchronousJoinTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,25 @@ public void shouldResumeSessionWithoutOpeningDialog() {
183183
verify(dialogAdapter, never()).showLoginDialog(eq(player), any(DialogWindowSpec.class));
184184
}
185185

186+
@Test
187+
public void shouldAutoLoginFromProxySessionWithoutCreatingLimbo() {
188+
// given
189+
Player player = mockPlayer("Bobby");
190+
setUpRegisteredJoin(player);
191+
given(proxySessionManager.shouldResumeSession("bobby")).willReturn(true);
192+
193+
// when
194+
asynchronousJoin.processJoin(player);
195+
196+
// then - uses forceLoginFromProxy (quiet, no redirect) so a concurrent BungeeReceiver
197+
// login does not cause an "already logged in" error
198+
verify(service).send(player, fr.xephi.authme.message.MessageKey.SESSION_RECONNECTION);
199+
verify(commandManager).runCommandsOnSessionLogin(player);
200+
verify(asynchronousLogin).forceLoginFromProxy(player);
201+
verify(asynchronousLogin, never()).forceLogin(player);
202+
verify(limboService, never()).createLimboPlayer(player, true);
203+
}
204+
186205
@Test
187206
public void shouldProcessPendingPreJoinLoginInsteadOfShowingDialog() {
188207
// given

authme-core/src/test/java/fr/xephi/authme/service/bungeecord/BungeeReceiverTest.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package fr.xephi.authme.service.bungeecord;
22

3+
import com.google.common.io.ByteArrayDataOutput;
4+
import com.google.common.io.ByteStreams;
35
import fr.xephi.authme.AuthMe;
46
import fr.xephi.authme.data.ProxySessionManager;
57
import fr.xephi.authme.process.Management;
8+
import fr.xephi.authme.security.HashUtils;
69
import fr.xephi.authme.service.BukkitService;
710
import fr.xephi.authme.settings.Settings;
811
import fr.xephi.authme.settings.properties.HooksSettings;
912
import org.bukkit.Server;
13+
import org.bukkit.entity.Player;
1014
import org.bukkit.plugin.messaging.Messenger;
1115
import org.junit.jupiter.api.BeforeEach;
1216
import org.junit.jupiter.api.Test;
@@ -19,6 +23,8 @@
1923
import static org.mockito.ArgumentMatchers.any;
2024
import static org.mockito.ArgumentMatchers.eq;
2125
import static org.mockito.BDDMockito.given;
26+
import static org.mockito.Mockito.mock;
27+
import static org.mockito.Mockito.never;
2228
import static org.mockito.Mockito.verify;
2329

2430
@ExtendWith(MockitoExtension.class)
@@ -77,4 +83,71 @@ void shouldUnregisterIncomingChannelWhenDisabledOnReload() {
7783
verify(messenger).registerIncomingPluginChannel(plugin, "authme:main", bungeeReceiver);
7884
verify(messenger).unregisterIncomingPluginChannel(plugin, "authme:main", bungeeReceiver);
7985
}
86+
87+
@Test
88+
void shouldQueueSessionAndForceLoginWhenPerformLoginReceivedForOnlinePlayer() {
89+
// given
90+
String sharedSecret = "test-secret";
91+
String playerName = "Bobby";
92+
long timestamp = System.currentTimeMillis();
93+
String hmac = HashUtils.hmacSha256(sharedSecret, playerName + ":" + timestamp);
94+
95+
given(settings.getProperty(HooksSettings.BUNGEECORD)).willReturn(true);
96+
given(settings.getProperty(HooksSettings.PROXY_SHARED_SECRET)).willReturn(sharedSecret);
97+
given(messenger.isIncomingChannelRegistered(plugin, "authme:main")).willReturn(false);
98+
99+
Player player = mock(Player.class);
100+
given(player.isOnline()).willReturn(true);
101+
given(bukkitService.getPlayerExact(playerName)).willReturn(player);
102+
103+
BungeeReceiver receiver =
104+
new BungeeReceiver(plugin, bukkitService, proxySessionManager, management, bungeeSender, settings);
105+
106+
byte[] payload = buildPerformLoginPayload(playerName, timestamp, hmac);
107+
108+
// when
109+
receiver.onPluginMessageReceived("authme:main", player, payload);
110+
111+
// then
112+
verify(proxySessionManager).processProxySessionMessage(playerName);
113+
verify(management).forceLoginFromProxy(player);
114+
verify(bungeeSender).sendAuthMeBungeecordMessage(player, MessageType.PERFORM_LOGIN_ACK);
115+
}
116+
117+
@Test
118+
void shouldOnlyQueueSessionWhenPerformLoginReceivedForOfflinePlayer() {
119+
// given
120+
String sharedSecret = "test-secret";
121+
String playerName = "Bobby";
122+
long timestamp = System.currentTimeMillis();
123+
String hmac = HashUtils.hmacSha256(sharedSecret, playerName + ":" + timestamp);
124+
125+
given(settings.getProperty(HooksSettings.BUNGEECORD)).willReturn(true);
126+
given(settings.getProperty(HooksSettings.PROXY_SHARED_SECRET)).willReturn(sharedSecret);
127+
given(messenger.isIncomingChannelRegistered(plugin, "authme:main")).willReturn(false);
128+
given(bukkitService.getPlayerExact(playerName)).willReturn(null);
129+
130+
BungeeReceiver receiver =
131+
new BungeeReceiver(plugin, bukkitService, proxySessionManager, management, bungeeSender, settings);
132+
133+
Player carrier = mock(Player.class);
134+
byte[] payload = buildPerformLoginPayload(playerName, timestamp, hmac);
135+
136+
// when
137+
receiver.onPluginMessageReceived("authme:main", carrier, payload);
138+
139+
// then
140+
verify(proxySessionManager).processProxySessionMessage(playerName);
141+
verify(management, never()).forceLoginFromProxy(any());
142+
verify(bungeeSender, never()).sendAuthMeBungeecordMessage(any(), any());
143+
}
144+
145+
private static byte[] buildPerformLoginPayload(String playerName, long timestamp, String hmac) {
146+
ByteArrayDataOutput out = ByteStreams.newDataOutput();
147+
out.writeUTF(MessageType.PERFORM_LOGIN.getId());
148+
out.writeUTF(playerName);
149+
out.writeLong(timestamp);
150+
out.writeUTF(hmac);
151+
return out.toByteArray();
152+
}
80153
}

0 commit comments

Comments
 (0)