diff --git a/README.md b/README.md
index 4ad4aca..aa5dc57 100644
--- a/README.md
+++ b/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
+
+```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.
diff --git a/authenticator.js b/authenticator.js
new file mode 100644
index 0000000..d17e75b
--- /dev/null
+++ b/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;
diff --git a/example.js b/example.js
new file mode 100644
index 0000000..27042e1
--- /dev/null
+++ b/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
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..f732aaf
--- /dev/null
+++ b/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 (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"
+ }
+}