We've incorporated two factor authentication into one of our applications using Google Authenticator. In QA, something really weird turned up. While I managed to fix it, I'm not really sure why the fix works.
For our shared secret, we assigned a GUID to the user when they begin the TFA setup. The GUID gets base-32 encoded, and put into the URL that is converted to a QR code and scanned by the user with their phone:
otpauth://totp/myapp_user?secret=g5swmnddhbtggllbgi3dsljumi3tallbmuytgljtg5sdgnbxmy2dgyjwmy======
And all works well for all non-ios machines we have tried. On ios alone, it throws a really peculiar error when trying to scan the barcode most of the time:
Invalid barcode
The barcode '[same as above]' is not a valid authentication token barcode.
It meets Google/RFC 4226's minimum secret requirement (128 bits), is properly Base32 encoded, etc... Why does it fail? The typical reason for this message is whitespace in the url -- but there is none.
If I add a small seed to the beginning of the guid everything works just fine:
otpauth://totp/myapp_user?secret=nfygq33omvzxky3lom3ggmzyha2tgnjnmu4gezbngqzdgyrnhbtdqzrnmeywimrwmjsgknzymi3a
Essentially it's the difference between:
secret = enc.Encode32(Encoding.ASCII.GetBytes("iphonesucks" + Guid.NewGuid().ToString())); // Works
secret = enc.Encode(Encoding.ASCII.GetBytes(Guid.NewGuid().ToString())); // Fails
newAuthUrl = string.Format("otpauth://totp/myapp_user?secret={0}", secret);
I've got two crazy theories on why this could possibly have worked:
The ios port requires more than 128 bits. My commentary/seed is enough to bump it over that limit, whatever that happens to be... except that I actually gave it more than 128 bits since it was a guid-as-string.
After Base32 decoding, the ios app recognizes the secret string as a guid and does something else with it.
I hate fixing a bug and not knowing why the fix worked. Can anyone explain this? Additional conspiracy theories on this topic are also welcome.
I was having the same issue as above. It turns out that Google Authenticator doesn't like = signs in the IPhone App, but doesn't complain in Android.
In my case I increased the length of the string before encoding to base32 from 8 chars to 10 chars. This removed the three === at the end of the string. I found this online as to why = signs appear in base32 encoded strings:
In your case above the same happened when you added the salt. The second secret you pasted doesn't have ='s at the end.
Hope this help.