speakeasyjs / speakeasy

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

Token from Google authenticator is never verified #91

Closed piggydoughnut closed 7 years ago

piggydoughnut commented 7 years ago

Hello smart people!

I am looking for some help making my implementation work. I read through similar issues but I didn't manage to find help there. I am writing a nodejs server based on Express and using google authenticator to generate codes. Here is my code.

const SPEAKEASY = require('speakeasy');
const QR_CODE = require('qrcode');

// initiating the 2 factor authentication, generating QR code
router.post('/signin/2factorAuth',  auth.authenticate(), (req, res) => {
    const SECRET = SPEAKEASY.generateSecret({
        length: 20
    });
    console.log(SECRET.base32);

       // saving the user secret
    req.user.twoFactorAuthSecretTemp = SECRET.base32;
    req.user.save()
        .then(() => {
            return QR_CODE.toDataURL(URL, (err, data_url) => {
                res.send(data_url);
            });
        });
});

// checking the token
router.get('/signin/auth_token', auth.authenticate(), (req, res) => {
    const USER_TOKEN = req.query.token;

        // temp solution for my timezone only
    const TIME = (MOMENT().unix() + 7200); // in seconds

    const TOKEN = SPEAKEASY.totp({
        secret: req.user.twoFactorAuthSecretTemp,
        encoding: 'base32',
        time: TIME
    });

    console.log('server TOKEN ' + TOKEN);

    const VERIFIED = SPEAKEASY.totp.verifyDelta({
        secret: req.user.twoFactorAuthSecretTemp,
        encoding: 'base32',
        token: USER_TOKEN,
        time: TIME
    });

    res.json(VERIFIED);
});

At first, I thought the problem was that my device time is different from server time. However after setting "time" option verify still returns undefined. My server time is UTC and device timezone is +2hrs.

auth.authenticate() - checks if the JWT token in the Authorization header is valid. It also sets the current logged in user to req.user

Any advice is highly appreciated.

Thank you and have a lovely day :D

piggydoughnut commented 7 years ago

Would be very grateful to get any help on this.

backmeupplz commented 7 years ago

@piggydoughnut I would suggest you logging req.user.twoFactorAuthSecretTemp inside /signin/auth_token route :) please read docs on Express about how req, res and next are generated and used :)

Long story short: the req.user from the first route is a completely different object from the req.user in the second route :)

piggydoughnut commented 7 years ago

@backmeupplz Hello) Thx to your reply. I am sorry, there is a bit of my code missing there. I am actually saving the user object in the first route so req.user.twoFactorAuthSecretTemp keys are definitely the same ones. I just updated my original question. However thank you for noting down the missing part.

piggydoughnut commented 7 years ago

If anyone ever gets stuck like I did, here is what was my problem. The implementation was correct and all was working fine. However the time on my Mac was not synced with the time on device. Even though, I am not sure now, but I think I the time seemed to be same on device and on a phone, coz it was one of the first things I checked.

Easy fix is to set both your Mac and iPhone to sync their time with Apple Europe server. You just gotta tick "set time and date automatically" and then all works perfectly fine :) My Google authenticator codes are validated and I m one very happy person now 😸

image