speakeasyjs / speakeasy

**NOT MAINTAINED** Two-factor authentication for Node.js. One-time passcode generator (HOTP/TOTP) with support for Google Authenticator.
MIT License
2.68k stars 229 forks source link

"list" argument must be an Array of Buffers #105

Open amreladawy opened 6 years ago

amreladawy commented 6 years ago

The following code is throwing an error "list" argument must be an Array of Buffers

speakeasy.hotp({ secret: 'ir6jqgomwnldmrhn', encoding: 'base32', counter: speakeasy._counter({}) })

I am doing this in an angular 5 app.

ghost commented 6 years ago

Having the same exact error in a React app on the verify.

blackhawkbigpunch commented 6 years ago

I got the same issue when I used 'base32' as encoding. Use 'ascii' or any other encoding. This is issue is caused because of this code:

 if (!Buffer.isBuffer(secret)) {
    secret = encoding === 'base32' ? base32.decode(secret)
      : new Buffer(secret, encoding);
  }

which makes the key string to buffer for further processing(creating hmac which needs 'secret'/'key' to be buffer ). As of now, I would suggest trying other encoding options.

amreladawy commented 6 years ago

Hi @blackhawkbigpunch , The point is that the key is generated at the server side and it is base32. The client must generate the correct topt and send it to the server for validation. I have to use base32 encoding.

mhingston commented 6 years ago

I had the same issue but resolved it by using master. The last released version is over 2 years old.

Sir-Chasington commented 6 years ago

Is speakeasy not supported anymore. Last update was a while ago

Prinzhorn commented 6 years ago

The point is that the key is generated at the server side and it is base32. The client must generate the correct topt and send it to the server for validation. I have to use base32 encoding.

@amreladawy you don't. It's just an encoding. You can let the server create a hex version of the same secret and use that on the client. I just had to do that to use the same secret in cypress testing code that runs in the browser.

On the server:

require('base32.js').decode('ir6jqgomwnldmrhn').toString('hex')
'447c9819ccb3563644ed'

Now you can do that on the client

speakeasy.hotp({
  secret: '447c9819ccb3563644ed',
  encoding: 'hex',
  counter: speakeasy._counter({})
})
amreladawy commented 6 years ago

Thanks @Prinzhorn Due to the long time it took to get a reply on this, I switched to jssha https://github.com/Caligatio/jsSHA which I had to use specific version 1.6.0 to get it working.

ygweric commented 6 years ago

const generateGoogleCode = secret => {
  const secretAscii = base32.decode(secret);
  const secretHex = toHex(secretAscii);
  const code = speakeasy.totp({
    secret: secretHex,
    algorithm: 'sha1',
    encoding: 'hex'
  });
  return code;
};

convert the base32 secret to ascii, then convert to hex, it will work

mustapha1509 commented 4 years ago

Solved, In "node_modules/speakeasy/index.js" replace from line 39 to 41 with this code

if (!Buffer.isBuffer(secret)) {
    secret = encoding === 'base32' ? new Buffer(base32.decode(secret)) : new Buffer(secret, encoding);
  }
zdenda-online commented 3 years ago

In case anyone had an issues with this. I used @ygweric code more or less but here I added full code (with dependencies and toHex implementation). For base32 decoding, I used `hi-base32: "^0.5.0" in my package.json

const secret = "...."; // retrieved from server as string in base32

const base32 = require('hi-base32');
const secretAscii = base32.decode(secret);
const secretHex = toHex(secretAscii);
const totp = speakeasy.totp({secret: secretHex, encoding: 'hex'});

function toHex(str) {
    let result = '';
    for (let i = 0; i < str.length; i++) {
        result += str.charCodeAt(i).toString(16);
    }
    return result;
}
joseneoito commented 8 months ago

@mustapha1509 thanks it worked for me