Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to use with google authenticator #7

Closed
scheler opened this issue Jul 24, 2017 · 25 comments
Closed

Unable to use with google authenticator #7

scheler opened this issue Jul 24, 2017 · 25 comments
Labels
bug Fix required released Code has been released to the package repository

Comments

@scheler
Copy link

scheler commented Jul 24, 2017

Hello,

I am unable to use this library to generate OTP matching Google Authenticator. The python version https://github.com/pyotp/pyotp works fine though.

Here's an example:

 <script src="https://yeojz.github.io/otplib/lib/otplib_commons.js"></script>
 <script src="https://rawgit.com/yeojz/otplib/gh-pages/lib/otplib.js"></script>

  var secret = "AAAABBBBCCCCDDDD";
  var token =  otplib.authenticator.generate(secret);
  console.log("secret is ", secret, "token is ", token);

This prints: secret is AAAABBBBCCCCDDDD token is 972373

The following python version matches the code generate by GA app:

>>> import pyotp 
>>> totp = pyotp.TOTP('AAAABBBBCCCCDDDD')
>>> totp.now()
u'580060'

Is there anything wrong with the above use of otplib for GA?

thanks
Santosh

@yeojz
Copy link
Owner

yeojz commented Jul 25, 2017

@scheler

From the snippets, for the python version, you're using TOTP. So the equivalent should be

otplib.totp.generate('AAAABBBBCCCCDDDD');

For Google Authenticator, although it uses TOTP under the hood, there are additional requirements for the secret. As such, you have to encode the secret before using it.

i.e.

var secret = otplib.authenticator.encode('AAAABBBBCCCCDDDD');
var token =  otplib.authenticator.generate(secret);

Let me know if you still have any problems with it.
Cheers.

@scheler
Copy link
Author

scheler commented Jul 26, 2017

@yeojz thanks for the response. I tried the following but none of the tokens match what GA app generates. Can you try this out and compare with what the GA mobile app generates, and see if the token matches for you?

 <html>
    <script src="https://yeojz.github.io/otplib/js/qrcode.min.js"></script>
    <script src="https://yeojz.github.io/otplib/lib/otplib_commons.js"></script>
    <script src="https://rawgit.com/yeojz/otplib/gh-pages/lib/otplib.js"></script>
   <script>

  var secret = "AAAABBBBCCCCDDDD";

  token =  otplib.totp.generate(secret);
  console.log("secret is ", secret, "totp token is ", token);
  token =  otplib.authenticator.generate(secret);
  console.log("secret is ", secret, "authenticator token is ", token);

  secret = otplib.authenticator.encode(secret);
  
  token =  otplib.totp.generate(secret);
  console.log("secret is ", secret, "totp token is ", token);
  token =  otplib.authenticator.generate(secret);
  console.log("secret is ", secret, "authenticator token is ", token);

</script>
</html>

@yeojz
Copy link
Owner

yeojz commented Jul 26, 2017

@scheler Noted... I'll need to investigate this over the weekend. Thanks

@yeojz
Copy link
Owner

yeojz commented Jul 29, 2017

@scheler

I've released v5.0.0 which should address your issue.

Sample code:

<html>

<head>

  <script src="https://rawgit.com/yeojz/otplib/gh-pages/lib/otplib-commons.js"></script>
  <script src="https://rawgit.com/yeojz/otplib/gh-pages/lib/otplib.js"></script>

  <body>
    <strong>Url</strong>
    <div class='url'></div>

    <strong>Token</strong>
    <div class='token'></div>

    <script>
      var secret = "AAAABBBBCC";

      secret = otplib.authenticator.encode(secret);
      var token = otplib.authenticator.generate(secret);

      console.log("secret is ", secret, "authenticator token is ", token);

      document.querySelector('.token').innerHTML = token;
      document.querySelector('.url').innerHTML = otplib.authenticator.keyuri('issue7', 'test', secret);
    </script>


  </body>

</html>

Do try it out and keep me posted. Thanks!

@yeojz yeojz added bug Fix required released Code has been released to the package repository labels Jul 29, 2017
@scheler
Copy link
Author

scheler commented Aug 3, 2017

@yeojz sorry I couldnt test this earlier. I tried this now - it seems to work on the sample secrets you have but not the one that I have, which I cannot share here. One difference I noticed between the two secrets is that the base32 decoded value of each is of different type. In the example that you have the secret is actually ASCII text AAAABBBBCC (which is the base32 decoded value of the secret in the key URI), but in my test case the base32 decoded value of secret in the key URI is binary.

I will try and obtain a test secret that I can share with you here.

@scheler
Copy link
Author

scheler commented Aug 9, 2017

@yeojz I am unable to obtain a test secret to share. I will have to debug this with the one I have.

I want to step through and compare result of each step against the python lib. Where can I get unobfuscated versions of the following JS files:

<script src="https://rawgit.com/yeojz/otplib/gh-pages/lib/otplib-commons.js"></script>
<script src="https://rawgit.com/yeojz/otplib/gh-pages/lib/otplib.js"></script>

@yeojz
Copy link
Owner

yeojz commented Aug 13, 2017

@scheler the files above are all generated by webpack.

The source of those files are in this repository under the src folder.

@SunburnedGoose
Copy link

I used the latest from npm and I was not able to get the otplib to generate a token that matched google authenticator.

@yeojz
Copy link
Owner

yeojz commented Aug 16, 2017

@SunburnedGoose

Mind if you try something?

  • Install v4.0.6 and see if that works for you since I tweaked it to address the issue brought up by the OP.
  • Ensure that your clocks on both devices are the same.

It is quite difficult for me to debug as RFC test cases are passing, and its working for the services I'm using, but it seems that there are still some token errors. If you are able to send me a sample fail case, that would be great.

At the very least, may I know the length of the secret you're using?

Thanks!

@SunburnedGoose
Copy link

SunburnedGoose commented Aug 16, 2017 via email

@SunburnedGoose
Copy link

My secret is 32 bytes.

@kohatang
Copy link

I have same issue, when I updated to v5.0.0 from v4.0.6.

The service is GitLab and the secret is 32 bytes.

@yeojz
Copy link
Owner

yeojz commented Aug 18, 2017

@SunburnedGoose @kohatang Noted. I'll try and replicate.

@kohatang Thanks for informing about the service!
Good that I have a service to debug against! That's very helpful!

This was referenced Aug 19, 2017
@yeojz
Copy link
Owner

yeojz commented Aug 19, 2017

@SunburnedGoose @kohatang @scheler
Narrowed down to the part where secret key being decoded as non-hex.

Do try out 5.1.1 and let me know if things are solved.

I tried it on my own GitLab account and things seems to be generating properly already.
Thanks

@kohatang
Copy link

I tried v5.1.1, It is working.
Thanks!!

@conorgil
Copy link

@yeojz I've been playing with otplib for about a day and was considering using it for a project because I love your documentation, you took the time to provider a browser version, and you seem pretty responsive to issues. Just wanted to let you know that I'm having the same issue with otplib generating incorrect codes compared to Google Authenticator, Authy, etc.

I've read through the suggestions in this thread and tried them all, but I cannot get it to work correctly. I am using otplib v6.0.3 like the following:

let secret = 'svt52xeze2twc2mu'.toUpperCase();
console.log('secret = %s', secret);
encoded_secret = otplib.authenticator.encode(secret);
console.log('code from otplib = %s', otplib.authenticator.generate(encoded_secret));

However, when I use speakeasy v2.0.0, it works as expected:

let secret = 'svt52xeze2twc2mu'.toUpperCase();
console.log('secret = %s', secret);
console.log('code from speakeasy = %s', speakeasy.totp({
  secret: secret,
  encoding: 'base32'
}));

And also otp v0.1.3 works as expected:

let secret = 'svt52xeze2twc2mu'.toUpperCase();
console.log('secret = %s', secret);
console.log('code from otp = %s', otp.parse(secret).totp());

Any thoughts on what I might be doing wrong, or do you think its a bug in otplib? I'm open to suggestions getting otplib working as I try to find another lib that works well in the browser and/or adapt speakeasy to work in the browser.

Thanks!

@yeojz yeojz added reopened and removed released Code has been released to the package repository labels Oct 13, 2017
@yeojz
Copy link
Owner

yeojz commented Oct 13, 2017

@conorgil Thanks for the examples. It'll help alot.

Going by the code for speakeasy, it will treat the secret as base32 and decode it.
Thus, for a 1:1 code to otplib, you would not need to encode again before passing to authenticator generate as it will already decode the secret by default. I'll update the docs so this is clearer.


update 1:

sha reference: d30a92b

For the code mismatch issue, I'll need some time to delve into it further.
I've tried swapping out the code generation, it returns correctly, but does not seem to to pass RFC 6238 tests for SHA256.

code:

/* eslint-disable no-console */
import otplib from './packages/otplib';
import speakeasy from 'speakeasy';
import otp from 'otp';

const secret = 'svt52xeze2twc2mu'.toUpperCase();
console.log('secret = %s', secret);

console.log('-------------------');
console.log('code from otplib = %s', otplib.authenticator.generate(secret));

console.log('-------------------');
console.log('code from speakeasy = %s', speakeasy.totp({
  secret: secret,
  encoding: 'base32'
}));

console.log('-------------------');
console.log('code from otp = %s', otp.parse(secret).totp());

otp-test


update 2

sha reference: 7a4478d

tests are passing. Giving it a once over before releasing.

@conorgil
Copy link

@yeojz thanks for the quick reply!

Going by the code for speakeasy, it will treat the secret as base32 and decode it.
Thus, for a 1:1 code to otplib, you would not need to encode again before passing to authenticator generate as it will already decode the secret by default. I'll update the docs so this is clearer.

That is what I had originally expected from the docs, so I don't think you need to update them. It did not work when I first tried to use the lib (perhaps because of the bug you just fixed), so I started reading through old issues on the project and thought I saw somewhere that someone said that you had to use otplib.authenticator.encode() first. That didn't make sense to me at the time (and makes even less sense after a good nights sleep), but I was debugging a bunch of libraries yesterday and gave it a shot anyways.

I will try the commit you referenced and let you know if its working for me as well. I assume I'll have to build it locally and import into my existing test project?

@yeojz
Copy link
Owner

yeojz commented Oct 14, 2017

@conorgil I've prereleased it on npm.. you can install it via npm install otplib@next

@conorgil
Copy link

@yeojz I checked out the master branch and there is a failing test. From commit 7fc4b4bbd6c43515d9e3339e8cddf3bfbec6b377:

Summary of all failing tests
 FAIL  packages/otplib-core/totpOptions.spec.js
  ● totpOptions › should return default options

    expect(received).toEqual(expected)

    Expected value to equal:
      {"algorithm": "sha1", "createHmacSecret": [Function totpSecret], "crypto": null, "digits": 6, "encoding": "ascii", "epoch": 1483228800000, "step": 30}
    Received:
      {"algorithm": "sha1", "createHmacSecret": [Function totpSecret], "crypto": null, "digits": 6, "encoding": "ascii", "epoch": 1483246800000, "step": 30}

    Difference:

    - Expected
    + Received

    @@ -2,8 +2,8 @@
       "algorithm": "sha1",
       "createHmacSecret": [Function totpSecret],
       "crypto": null,
       "digits": 6,
       "encoding": "ascii",
    -  "epoch": 1483228800000,
    +  "epoch": 1483246800000,
       "step": 30,
     }

      at Object.<anonymous> (packages/otplib-core/totpOptions.spec.js:27:42)
          at Promise (<anonymous>)
      at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)
          at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:188:7)


Test Suites: 1 failed, 33 passed, 34 total
Tests:       1 failed, 194 passed, 195 total
Snapshots:   0 total
Time:        1.556s, estimated 3s

Also, I tried installing the next release for my test script and it still does not work as expected:

$> npm install otplib@next
npm WARN otplib No description
npm WARN otplib No repository field.
npm WARN otplib No license field.

+ otplib@7.0.0-0
removed 68 packages and updated 1 package in 0.916s
$> node main.js
secret = svt52xeze2twc2mu
code from otplib =    922013
code from speakeasy = 181503
code from otp =       181503

@conorgil
Copy link

I ran the tests on branch hotfix/code-gen and the same tests fails also with problems comparing the epoch

@yeojz
Copy link
Owner

yeojz commented Oct 17, 2017

@conorgil By any chance you're using node 8?

It puzzled me for a while as I couldn't reproduce the issue of the tests, but it seems that swapping to node 8 triggered it.. seems like there is some difference to Date mocks using node 8. I'll dig more into it.

I am however, unable to repro the code issue.

➜ node ./lib-test.js
secret = svt52xeze2twc2mu
1508258587427
-------------------
code from otplib = 126562
-------------------
code from speakeasy = 126562
-------------------
code from otp = 126562

update 1

I've fix the test within the hotfix/code-gen branch. It was due to the difference in handling new Date with timezone.

@conorgil
Copy link

@yeojz thanks for your continued work on this. Yea, I'm using the latest version of Node. Sorry for not specifying that in my initial comment.

$> node --version
v8.7.0

@yeojz
Copy link
Owner

yeojz commented Oct 22, 2017

@conorgil

I've added the example you've given into the test suite under issues.spec.js,
with the token cross-checked with both otp and speakeasy libraries.

Since it's clearing the automated checks, I'm releasing the changes under v7.0.0.

@yeojz
Copy link
Owner

yeojz commented Feb 14, 2018

Closing this as no further issues are raised.
Thanks all!

@yeojz yeojz closed this as completed Feb 14, 2018
@yeojz yeojz added released Code has been released to the package repository and removed reopened labels Feb 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Fix required released Code has been released to the package repository
Projects
None yet
Development

No branches or pull requests

5 participants