I'm using this code from GitHub
https://github.com/as08/ClassicASP.TwoFactorAuthentication
I downloaded the demo site, installed what I needed on the server and everything works perfectly. The demo site has a lot of code so I broke it down into the two components that are shown on the Github page 1)Generating a secret key and QR code 2) Validating a verification code.
I made 2 very barebones basic asp pages
index.asp
<%
Dim TwoFA : Set TwoFA = Server.CreateObject("ClassicASP.TwoFactorAuthentication")
TwoFA.SecretKeyLength(20)
TwoFA.HashMode("SHA1")
TwoFA.totpSize(6)
RecoveryPasswordLength = 18
Dim SecretKey, RecoveryPassword, GenerateQR
SecretKey = TwoFA.GenerateSecretKey()
RecoveryPassword = TwoFA.RecoveryPassword(RecoveryPasswordLength)
response.write("Secret Key: " & secretKey & "<br>")
response.write("Recovery Password: " & RecoveryPassword & "<br />")
' Generate the QR code
GenerateQR = "<img src=""https://chart.googleapis.com/chart" &_
"?chs=320x320" &_
"&chld=H|0" &_
"&cht=qr" &_
"&chl=" & Server.URLencode("otpauth://totp/test@test.com" &_
"?secret=" & SecretKey &_
"&issuer=examplesite.com" &_
"&algorithm=SHA1" &_
"&digits=6" &_
"&period=30") & "&choe=UTF-8"" " &_
"class=""img-fluid border-info border mt-4 QRframe"" " &_
"width=""320px"" height=""320px"">"
Set TwoFA = Nothing
%>
<%=GenerateQR%>
Validate.asp
<%
Dim TwoFA : Set TwoFA = Server.CreateObject("ClassicASP.TwoFactorAuthentication")
TwoFA.SecretKeyLength(20)
TwoFA.HashMode("SHA1")
TwoFA.totpSize(6)
TOTP = request("totp")
response.write(totp & "<br />")
If TwoFA.Verify("EDSLKFQENTEFPATYN5LAZ5BCGD2UOR4R",cStr(TOTP)) Then
' Valid Time-based One-time Password (TOTP)
response.write("valid")
Else
' Invalid TOTP
response.write("invalid")
End If
Set TwoFA = Nothing
%>
To test, I went to my index.asp page and generated a QRcode and set that up in Microsoft Authenticator. I then took the secret key and hardcoded it into the validate page's verify call. Then I went into Authenticator and got the code and tested it by going to validate.asp?totp=123456
. No matter what I do, I can't get this to work. I always get the response.write("invalid")
result.
Why is this not returning a valid response even though I'm typing in the right 6 digit code using the right secret key?
This isn't an answer as such, but it's too long and detailed to leave as a comment.
I've downloaded and run the demo from my GitHub repository, and used Microsoft authenticator, Google Authenticator and Authy. And they all work fine, including the 30 second tolerance. I even changed my time zone to various different ones (although that wouldn't account for the 4 second expiration you described, and they didn't. I was just covering all my bases).
All I can think to recommend is this, download and run this UTC.asp
page:
<%=UTC_DateTime()%>
<script language="JScript" runat="server">
// Return the current UTC date and time regardless of what timezone the server is set to
function UTC_DateTime() {
var date = new Date();
// date.getUTCMonth() returns a value from 0 - 11 (?) so we need to + 1
var result = date.getUTCFullYear() + "-" + (date.getUTCMonth() + 1) + "-" + date.getUTCDate() + " " + date.getUTCHours() + ":" + date.getUTCMinutes() + ":" + date.getUTCSeconds();
// Pad month/day/hour/minute/second values with a 0 if necessary
return result.replace(/(\D)(\d)(?!\d)/g, "$10$2");
}
</script>
Then compare it to a site like timeanddate.com, if the times aren't the same (give or take a few a few seconds), then there has to be something wrong with you servers time settings, it must be generating a UTC time that is out of sync. Some sort of NTP glitch?
If that's the case, I don't have an answer, hopefully someone does, but that has to be the problem given it's working fine for me. (I ran all apps on iOS and the demo on Windows 10 and Windows Server 2016 just to clarify)