problems with 2FA and Google Authenticator
artshishkin opened this issue · 2 comments
It seems like Google Authenticator skips period
parameter in QR Code image like this:
<img src="https://api.qrserver.com/v1/create-qr-code/?data=otpauth%3A%2F%2Ftotp%2FSFG%3Aspring%3Fsecret%3DKYWDNNMYH57XUPL6N4YORD6DTYD7ZDO2%26issuer%3DSFG%26algorithm%3DSHA1%26digits%3D6%26period%3D60&size=200x200&ecc=M&margin=0"/>
So if period is different from default (30s) Google Authenticator gives wrong Verification Code and our server can not verify it:
if (googleAuthenticator.authorizeUser(user.getUsername(), verifyCode)) {
User savedUser = userRepository.findById(user.getId()).orElseThrow();
savedUser.setUseGoogle2f(true);
userRepository.save(savedUser);
return "/index";
}
Method googleAuthenticator.authorizeUser(user.getUsername(), verifyCode)
gives false
result.
But application FreeOTP
from Red Hat gives correct Verification Code and has no issue in verification.
Despite we config our period to 60s Google Authenticator makes one full circle in 30 seconds. But FreeOTP makes it in 60 seconds.
And Wikipedia says:
Subsequently, when the user opens the Authenticator app, it calculates an HMAC-SHA1 hash value using this secret key. The message that is HMAC-ed can be:
the number of 30-second periods since the Unix epoch (TOTP); or
a counter that is incremented with each new code (HOTP).
A portion of the HMAC is extracted and displayed to the user as a six-digit code.
So you can either choose FreeOTP
or change config to default period:
.setTimeStepSizeInMillis(TimeUnit.SECONDS.toMillis(30))
in
@Bean
public GoogleAuthenticator googleAuthenticator(ICredentialRepository credentialRepository){
GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder configBuilder
= new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder();
configBuilder
.setTimeStepSizeInMillis(TimeUnit.SECONDS.toMillis(30))
.setWindowSize(10)
.setNumberOfScratchCodes(0);
GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(configBuilder.build());
googleAuthenticator.setCredentialRepository(credentialRepository);
return googleAuthenticator;
}