Not working with Authy
See original GitHub issueHaving a few issues getting this working with Authy. It works with Google Authenticator no problem. If I have Authenticator and Authy scan the same QR code they’ll give me different OTPs, with Authy’s being incorrect.
Using the latest npm version of speakeasy and qrcode on Node v8.6.0.
generating the secret/url
const speakeasy = require('speakeasy');
const qr = require('qrcode');
const email = 'user@email.com';
const {ascii: secret} = speakeasy.generateSecret({
issuer: 'MyApp',
length: 128,
name: email
});
let url = speakeasy.otpauthURL({
algorithm: 'sha512',
issuer: 'MyApp',
digits: 8,
secret,
label: email
});
// Convert otpauth url to qr code url
url = await new Promise((resolve, reject) =>
qr.toDataURL(url, (e, u) => e ? reject(e) : resolve(u))
);
req.session.otpTempSecret = secret;
verifying the token
const verified = speakeasy.totp.verify({
algorithm: 'sha512',
secret: req.session.otpTempSecret,
digits: 8,
token: req.body.token.replace(/\D/g, '')
});
if (!verified) throw 'Invalid token';
Only on Authy am I having two issues:
- There is no issuer. ‘MyApp’ is not shown, only the user’s email that was passed as a label.
- It generates incorrect codes.
I also tried loading the latest version of speakeasy from Github but there was no change to either issues.
Any ideas what I’m doing wrong? I’m assuming somewhere there’s a communication issue between my implementation of speakeasy and Authy as I have no problem with speakeasy on Google Authenticator and no problem with Authy on other sites. I don’t really care about the issuer not showing on Authy but generating incorrect codes is a bit of problem…
Issue Analytics
- State:
- Created 6 years ago
- Reactions:1
- Comments:29
Top GitHub Comments
Based on this documentation about the otpauth URL syntax, the recommendation seems to be that you include the
issuer
both as anissue
key in the otpauthURL options and as the prefix to thelabel
key.We also recently discovered that Authy and Google Authenticator on iOS will reject the otpauth URL if the
issuer
-portion of thelabel
contains a space. (I imagine it would reject it if there is any non-encoded space in thelabel
, but I’ve only tested it in theissuer
portion of thelabel
.)We had been doing this…
… but I’m about to change to the following in an attempt to fix this iOS problem (which, as a side note, I probably should have been doing all along). Note the uses of
encodeURIComponent()
:Here is an example output from
speakeasy.otpauthURL(otpAuthUrlOptions);
:otpauth://totp/Has%20Space:First%20Last?secret=HJ2VWR3RF4SEMNZJ&issuer=Has%20Space
All, sorry for the late response here. I’ve been busy with work and haven’t been able to make plans for the next release because of the breaking changes it would introduce, but at the least I think I can help with your issue, @LukeXF.
@LukeXF: Based on your latest comment, you are generating a secret that has a base32 encoding that starts with
FZBWG4J7...
. The key issue here is that when you create your own otpauth:// URL withotpauthURL()
, you are passing in the base32 secret without specifying the encoding for the secret. By default,otpauthURL()
assumes a secret that is passed in without anencoding
argument is ASCII-encoded, so it will use base32 to convert it again. (docs)You can see this if you try to base32-encode
FZBWG4J7...
– you’ll get the secret that you see in the otpauth:// URL,IZNEEV2H...
. This is also why thesecret.otpauth_url
that you get back fromgenerateSecret()
is working correctly, even in Authy, since it uses the correct base32-encoded URL. The solution to this is to pass in theencoding
parameter asbase32
which will bypass the conversion tobase32
. Hope this helps.I still need to look into why this is not working with Authy. It absolutely should support Authy and any other system that implements the spec.
(Sorry for closing the issue – that was accidental.)