- Make TotpAuthenticator keep track of the used codes and reject any known ones
This commit is contained in:
@@ -59,7 +59,7 @@ public class GenerateTotpService implements HasCleanup {
|
||||
*/
|
||||
public boolean isTotpCodeCorrectForGeneratedTotpKey(Player player, String totpCode) {
|
||||
TotpGenerationResult totpDetails = totpKeys.get(player.getName().toLowerCase());
|
||||
return totpDetails != null && totpAuthenticator.checkCode(totpDetails.getTotpKey(), totpCode);
|
||||
return totpDetails != null && totpAuthenticator.checkCode(player.getName(), totpDetails.getTotpKey(), totpCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
package fr.xephi.authme.security.totp;
|
||||
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.Table;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.warrenstrange.googleauth.GoogleAuthenticator;
|
||||
import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
|
||||
import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator;
|
||||
import com.warrenstrange.googleauth.IGoogleAuthenticator;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.initialization.HasCleanup;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static fr.xephi.authme.util.Utils.MILLIS_PER_MINUTE;
|
||||
|
||||
/**
|
||||
* Provides TOTP functions (wrapping a third-party TOTP implementation).
|
||||
*/
|
||||
public class TotpAuthenticator {
|
||||
public class TotpAuthenticator implements HasCleanup {
|
||||
|
||||
private static final int CODE_RETENTION_MINUTES = 5;
|
||||
|
||||
private final IGoogleAuthenticator authenticator;
|
||||
private final BukkitService bukkitService;
|
||||
private final Table<String, Integer, Long> usedCodes = HashBasedTable.create();
|
||||
|
||||
@Inject
|
||||
TotpAuthenticator(BukkitService bukkitService) {
|
||||
@@ -33,19 +41,26 @@ public class TotpAuthenticator {
|
||||
}
|
||||
|
||||
public boolean checkCode(PlayerAuth auth, String totpCode) {
|
||||
return checkCode(auth.getTotpKey(), totpCode);
|
||||
return checkCode(auth.getNickname(), auth.getTotpKey(), totpCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given input code matches for the provided TOTP key.
|
||||
*
|
||||
* @param playerName the player name
|
||||
* @param totpKey the key to check with
|
||||
* @param inputCode the input code to verify
|
||||
* @return true if code is valid, false otherwise
|
||||
*/
|
||||
public boolean checkCode(String totpKey, String inputCode) {
|
||||
public boolean checkCode(String playerName, String totpKey, String inputCode) {
|
||||
String nameLower = playerName.toLowerCase();
|
||||
Integer totpCode = Ints.tryParse(inputCode);
|
||||
return totpCode != null && authenticator.authorize(totpKey, totpCode);
|
||||
if (totpCode != null && !usedCodes.contains(nameLower, totpCode)
|
||||
&& authenticator.authorize(totpKey, totpCode)) {
|
||||
usedCodes.put(nameLower, totpCode, System.currentTimeMillis());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public TotpGenerationResult generateTotpKey(Player player) {
|
||||
@@ -55,6 +70,12 @@ public class TotpAuthenticator {
|
||||
return new TotpGenerationResult(credentials.getKey(), qrCodeUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performCleanup() {
|
||||
long threshold = System.currentTimeMillis() - CODE_RETENTION_MINUTES * MILLIS_PER_MINUTE;
|
||||
usedCodes.values().removeIf(value -> value < threshold);
|
||||
}
|
||||
|
||||
public static final class TotpGenerationResult {
|
||||
private final String totpKey;
|
||||
private final String authenticatorQrCodeUrl;
|
||||
|
||||
Reference in New Issue
Block a user