Browse Source

v1.0.0

master v1.0.0
AJ ONeal 9 years ago
parent
commit
c16ce07792
  1. 104
      README.md
  2. 59
      authenticator.js
  3. 24
      example.js
  4. 36
      package.json

104
README.md

@ -1,2 +1,102 @@
# node-authenticator
Two- / Multi- Factor Authenication (2FA / MFA) for node.js
Node.js Authenticator
=====================
Two- and Multi- Factor Authenication (2FA / MFA) for node.js
There are a number of apps that various websites use to give you 6-digit codes to increase security when you log in:
* Authy [iPhone](https://itunes.apple.com/us/app/authy/id494168017?mt=8) [Android](https://play.google.com/store/apps/details?id=com.authy.authy&hl=en) [Chrome](https://chrome.google.com/webstore/detail/authy/gaedmjdfmmahhbjefcbgaolhhanlaolb?hl=en) [Linux](https://www.authy.com/personal/) [OS X](https://www.authy.com/personal/) [BlackBerry](https://appworld.blackberry.com/webstore/content/38831914/?countrycode=US&lang=en)
* Google Authenticator [iPhone](https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8) [Android](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en)
* Microsoft Authenticator [Windows Phone](https://www.microsoft.com/en-us/store/apps/authenticator/9wzdncrfj3rj) [Android](https://play.google.com/store/apps/details?id=com.microsoft.msa.authenticator) [iPhone]()
* GAuth [FxOS](https://marketplace.firefox.com/app/gauth/)
There are many [Services that Support MFA](http://lifehacker.com/5938565/heres-everywhere-you-should-enable-two-factor-authentication-right-now),
including Google, Microsoft, Facebook, Digital Ocean, for starters.
This module uses [`notp`](https://github.com/guyht/notp) which implements `TOTP` [(RFC 6238)](https://www.ietf.org/rfc/rfc6238.txt)
(the *Authenticator* standard), which is based on `HOTP` [(RFC 4226)](https://www.ietf.org/rfc/rfc4226.txt)
to provide codes that are exactly compatible with all other *Authenticator* apps and services that use them.
Usage
=====
```bash
npm install authenticator --save
```
```javascript
'use strict';
var authenticator = require('authenticator');
var formattedKey = authenticator.generateKey();
// "acqo ua72 d3yf a4e5 uorx ztkh j2xl 3wiz"
var formattedToken = authenticator.generateToken(formattedKey);
// "957 124"
authenticator.verifyToken(formattedKey, formattedToken);
// { delta: 0 }
authenticator.verifyToken(formattedKey, '000 000');
// null
```
QRCode
------
See <https://davidshimjs.github.io/qrcodejs/>
```javascript
'use strict';
var el = document.querySelector('.js-qrcode-canvas');
var link = "otpauth://totp/{{NAME}}?secret={{KEY}}";
var name = "Your Service";
// remove spaces, hyphens, equals, whatever
var key = "acqo ua72 d3yf a4e5 uorx ztkh j2xl 3wiz".replace(/\W/g, '').toLowerCase();
var qr = new QRCode(el, {
text: link.replace(/{{NAME}}/g, name).replace(/{{KEY}}/g, key)
});
```
Formatting
----------
All non-alphanumeric characters are ignored, so you could just as well use hyphens
or periods or whatever suites your use case.
These are just as valid:
* "acqo ua72 d3yf a4e5 - uorx ztkh j2xl 3wiz"
* "98.24.63"
0, 1, 8, and 9 also not used (so that base32).
To further avoid confusion with O, o, L, l, I, B, and g
you may wish to display lowercase instead of uppercase.
TODO: should this library replace 0 with o, 1 with l (or I?), 8 with b, 9 with g, and so on?
90-second Window
----------------
The window is set to +/- 1, meaning each token is valid for a total of 90 seconds
(-30 seconds, +0 seconds, and +30 seconds)
to account for time drift (which should be very rare for mobile devices)
and humans who are handicapped or otherwise struggle with quick fine motor skills (like my grandma).
Why not SpeakEasy?
------------------
I took a look at the code and I didn't feel comfortable using it.
For any module related to security I want to see that the code is clean,
well-maintained, and that any security-related bugs are addressed.
The author was obviously not well-versed in JavaScript at the time
that he wrote it and it hasn't been cleaned up since.
Also, the author hasn't been responsive to issues and pull requests.
The notp author has been responsive, but notp doesn't do everything I would like.

59
authenticator.js

@ -0,0 +1,59 @@
'use strict';
var crypto = require('crypto');
var b32 = require('thirty-two');
// Generate a key
function generateOtpKey() {
// 20 cryptographically random binary bytes (160-bit key)
var key = crypto.randomBytes(20);
return key;
}
// Text-encode the key as base32 (in the style of Google Authenticator - same as Facebook, Microsoft, etc)
function encodeGoogleAuthKey(bin) {
// 32 ascii characters without trailing '='s
var base32 = b32.encode(bin).toString('utf8').replace(/=/g, '');
// lowercase with a space every 4 characters
var key = base32.toLowerCase().replace(/(\w{4})/g, "$1 ").trim();
return key;
}
function generateGoogleAuthKey() {
return encodeGoogleAuthKey(generateOtpKey());
}
// Binary-decode the key from base32 (Google Authenticator, FB, M$, etc)
function decodeGoogleAuthKey(key) {
// decode base32 google auth key to binary
var unformatted = key.replace(/\W+/g, '').toUpperCase();
var bin = b32.decode(unformatted);
return bin;
}
// Generate a Google Auth Token
function generateGoogleAuthToken(key) {
var bin = decodeGoogleAuthKey(key);
var notp = require('notp');
return notp.totp.gen(bin);
}
// Verify a Google Auth Token
function verifyGoogleAuthToken(key, token) {
var bin = decodeGoogleAuthKey(key);
var notp = require('notp');
token = token.replace(/\W+/g, '');
// window is +/- 1 period of 30 seconds
return notp.totp.verify(token, bin, { window: 1, time: 30 });
}
module.exports.generateKey = generateGoogleAuthKey;
module.exports.generateToken = generateGoogleAuthToken;
module.exports.verifyToken = verifyGoogleAuthToken;

24
example.js

@ -0,0 +1,24 @@
'use strict';
var authenticator = require('./authenticator');
var formattedKey = authenticator.generateKey();
console.log(formattedKey);
// "acqo ua72 d3yf a4e5 uorx ztkh j2xl 3wiz"
var formattedToken = authenticator.generateToken(formattedKey);
console.log(formattedToken);
// "957 124"
var result = authenticator.verifyToken(formattedKey, formattedToken);
console.log(result);
// { delta: 0 }
result = authenticator.verifyToken(formattedKey, '000-000');
console.log(result);
// null
// result will allways be one of
// (failure) null
// (success) { delta: -1 }, { delta: 0 }, or { delta: 1 }
// delta lets you know which way time drift is occurring

36
package.json

@ -0,0 +1,36 @@
{
"name": "authenticator",
"version": "1.0.0",
"description": "Two- / Multi- Factor Authenication (2FA / MFA) for node.js",
"main": "authenticator.js",
"scripts": {
"test": "node example.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Daplie/node-authenticator.git"
},
"keywords": [
"authenticator",
"2fa",
"mfa",
"token",
"key",
"base32",
"code",
"generator",
"authy",
"google",
"microsoft"
],
"author": "AJ ONeal <coolaj86@gmail.com> (http://coolaj86.com/)",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/Daplie/node-authenticator/issues"
},
"homepage": "https://github.com/Daplie/node-authenticator#readme",
"dependencies": {
"notp": "^2.0.3",
"thirty-two": "0.0.2"
}
}
Loading…
Cancel
Save