springframeworkguru/ssc-brewery

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&amp;size=200x200&amp;ecc=M&amp;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.

IMG_20201102_161736

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;
    }