Security-hardened TOTP for Java
<dependency>
<groupId>io.github.pratiyush</groupId>
<artifactId>totp-lib</artifactId>
<version>1.0.3</version>
</dependency>
implementation("io.github.pratiyush:totp-lib:1.0.3")
implementation 'io.github.pratiyush:totp-lib:1.0.3'
import io.github.pratiyush.totp.*;
// 1. Generate a secret for a new user
String secret = SecretGenerator.generate(Algorithm.SHA256);
// 2. Create TOTP instance
TOTP totp = TOTP.defaultInstance();
// 3. Generate a code
String code = totp.generate(secret);
// 4. Verify user input
boolean valid = totp.verify(secret, userCode);
Prevent the same TOTP code from being used twice within its validity window.
import io.github.pratiyush.totp.TOTP;
import java.time.Duration;
// Create TOTP with replay protection (2-minute window)
TOTP totp = TOTP.builder()
.withReplayProtection(Duration.ofMinutes(2))
.build();
// Verify with user ID for per-user tracking
boolean valid = totp.verify(secret, code, userId);
// Same code + same userId = rejected on second attempt
Generate QR codes compatible with Google Authenticator, Microsoft Authenticator, Authy, and others.
import io.github.pratiyush.totp.QRCodeGenerator;
import java.nio.file.Path;
// Save QR code as PNG file
QRCodeGenerator.saveToFile(
secret, "user@example.com", "MyApp",
Path.of("qr.png"), 250
);
// Or get as Base64 for embedding in HTML
String base64 = QRCodeGenerator.generateBase64(
secret, "user@example.com", "MyApp", 250);
// Or get a data URI ready for <img src="...">
String dataUri = QRCodeGenerator.generateDataUri(
secret, "user@example.com", "MyApp", 250);
// Build otpauth:// URI manually
String uri = QRCodeGenerator.buildOtpauthUri(
secret, "user@example.com", "MyApp",
Algorithm.SHA256, 6, 30);
// Full custom config
TOTPConfig config = TOTPConfig.builder()
.algorithm(Algorithm.SHA256) // SHA1, SHA256, SHA512
.digits(8) // 6-8 digits
.periodSeconds(30) // 15-120 seconds
.allowedDrift(1) // time window tolerance
.build();
TOTP totp = TOTP.builder()
.config(config)
.build();
// Google Authenticator compatible (SHA-1, 6 digits, 30s)
TOTPConfig.defaultConfig();
// Recommended for new apps (SHA-256, 6 digits, 30s)
TOTPConfig.sha256Config();
// Maximum security (SHA-512, 8 digits, 30s)
TOTPConfig.highSecurityConfig();
Secrets are automatically cleared from memory when no longer needed.
// SecureBytes implements AutoCloseable
try (SecureBytes secret = SecureBytes.wrap(rawSecretBytes)) {
boolean valid = engine.verify(secret.getBytes(), code);
} // secret bytes are zeroed out here automatically
// Never log or serialize secrets
// SecureBytes.toString() returns "[REDACTED]"
@Service
public class TwoFactorAuthService {
private final TOTP totp = TOTP.builder()
.algorithm(Algorithm.SHA256)
.withReplayProtection(Duration.ofMinutes(2))
.build();
public String enrollUser(String userId) {
String secret = SecretGenerator.generate(Algorithm.SHA256);
// Store secret encrypted in database
userRepository.saveTotpSecret(userId, encrypt(secret));
return secret;
}
public boolean verifyCode(String userId, String code) {
String secret = decrypt(userRepository.getTotpSecret(userId));
return totp.verify(secret, code, userId);
}
public String getQrCodeDataUri(String userId, String email) {
String secret = decrypt(userRepository.getTotpSecret(userId));
return QRCodeGenerator.generateDataUri(
secret, email, "MyApp", 250);
}
}
All comparisons use constant-time algorithms to prevent timing attacks.
Built-in ReplayGuard prevents the same code from being used twice.
Core TOTP requires no external libraries. QR and logging are optional.
Full compliance with TOTP (RFC 6238) and HOTP (RFC 4226) standards.
Works with Google Authenticator, Microsoft Authenticator, Authy, and more.
SecureBytes wrapper automatically clears secrets from memory after use.
| Method | Description |
|---|---|
generate(secret) | Generate code for current time |
generateAt(secret, instant) | Generate code for specific time |
verify(secret, code) | Verify a code |
verify(secret, code, userId) | Verify with replay protection |
getSecondsRemaining() | Seconds until current code expires |
| Method | Description |
|---|---|
generate() | Generate 160-bit secret |
generate(algorithm) | Generate algorithm-appropriate secret |
isValid(secret) | Validate a Base32 secret |
| Method | Description |
|---|---|
generateBase64(...) | Generate Base64 PNG |
generateDataUri(...) | Generate data URI for HTML |
saveToFile(...) | Save to file |
buildOtpauthUri(...) | Build otpauth:// URI |
| Algorithm | Key Size | Use Case |
|---|---|---|
| SHA-1 | 20 bytes | Legacy compatibility |
| SHA-256 | 32 bytes | Recommended |
| SHA-512 | 64 bytes | Maximum security |