From f55cd310c6e90abfb98123e9d3b56624a7ab7cb4 Mon Sep 17 00:00:00 2001 From: Rishabh Venu Date: Wed, 7 Oct 2026 20:52:07 -0400 Subject: [PATCH] fix(totp): allow /totp add when the server name contains a colon The server name is passed to googleauth as the TOTP issuer. The library throws "Issuer cannot contain the ':' character." for such a name, so /totp add failed with an unexpected error for every player. The otpauth label has the form "issuer:account", so a colon cannot be part of the issuer. Replace it with a hyphen when building the URL. The configured server name itself is not changed. Fixes #3158 --- .../security/totp/TotpAuthenticator.java | 5 +++-- .../security/totp/TotpAuthenticatorTest.java | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/authme-core/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java b/authme-core/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java index c7256af9e0..f6df3bc6b3 100644 --- a/authme-core/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java +++ b/authme-core/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java @@ -69,8 +69,9 @@ public boolean checkCode(String playerName, String totpKey, String inputCode) { public TotpGenerationResult generateTotpKey(Player player) { GoogleAuthenticatorKey credentials = authenticator.createCredentials(); - String otpAuthUrl = GoogleAuthenticatorQRGenerator.getOtpAuthTotpURL( - settings.getProperty(PluginSettings.SERVER_NAME), player.getName(), credentials); + // The otpauth label is "issuer:account", so the issuer must not contain a colon + String issuer = settings.getProperty(PluginSettings.SERVER_NAME).replace(':', '-'); + String otpAuthUrl = GoogleAuthenticatorQRGenerator.getOtpAuthTotpURL(issuer, player.getName(), credentials); String qrCodeUrl = "https://api.qrserver.com/v1/create-qr-code/?data=" + URLEncoder.encode(otpAuthUrl, StandardCharsets.UTF_8) + "&size=200x200&ecc=M&margin=10"; diff --git a/authme-core/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java b/authme-core/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java index da45004cf6..ac6c3ea937 100644 --- a/authme-core/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java @@ -15,6 +15,9 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + import static fr.xephi.authme.AuthMeMatchers.stringWithLength; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -69,6 +72,25 @@ void shouldGenerateTotpKey() { assertThat(key1.getTotpKey(), not(equalTo(key2.getTotpKey()))); } + @Test + void shouldGenerateTotpKeyForServerNameWithColon() { + // given + totpAuthenticator = new TotpAuthenticator(settings); + + Player player = mock(Player.class); + given(player.getName()).willReturn("Bobby"); + given(settings.getProperty(PluginSettings.SERVER_NAME)).willReturn("UT:Rebirth"); + + // when + TotpGenerationResult key = totpAuthenticator.generateTotpKey(player); + + // then + assertThat(key.getTotpKey(), stringWithLength(32)); + assertThat(key.getAuthenticatorQrCodeUrl(), containsString(URLEncoder.encode( + "otpauth://totp/UT-Rebirth:Bobby?secret=" + key.getTotpKey() + "&issuer=UT-Rebirth&", + StandardCharsets.UTF_8))); + } + @Test void shouldCheckCodeAndDeclareItValidOnlyOnce() { // given