Two- / Multi- Factor Authenication (2FA / MFA / OTP) for browser JavaScript
Go to file
AJ ONeal 407521782c auto-update banner 2016-12-30 02:38:28 -07:00
demo #1 add issuer to demo (for google auth) 2016-09-02 12:42:32 -06:00
examples move demo files to ./demo 2015-11-03 00:24:16 -08:00
.gitignore ignore bower_components 2015-10-23 00:14:51 -07:00
LICENSE Initial commit 2015-10-22 18:29:05 -07:00
README.md auto-update banner 2016-12-30 02:38:28 -07:00
authenticator.js add generateTotpUri 2015-11-03 00:31:06 -08:00
bower.json bump 2015-11-03 00:31:24 -08:00
index.html #1 add issuer to demo (for google auth) 2016-09-02 12:44:57 -06:00
test.html move demo files to ./demo 2015-11-03 00:24:16 -08:00

README.md

About Daplie: We're taking back the Internet!

Down with Google, Apple, and Facebook!

We're re-decentralizing the web and making it read-write again - one home cloud system at a time.

Tired of serving the Empire? Come join the Rebel Alliance:

jobs@daplie.com | Invest in Daplie on Wefunder | Pre-order Cloud, The World's First Home Server for Everyone

Browser Authenticator

Two- / Multi- Factor Authenication (2FA / MFA) for browser JavaScript

There are a number of apps that various websites use to give you 6-digit codes to increase security when you log in:

There are many Services that Support MFA, including Google, Microsoft, Facebook, and Digital Ocean for starters.

This module uses botp which implements TOTP (RFC 6238) (the Authenticator standard), which is based on HOTP (RFC 4226) to provide codes that are exactly compatible with all other Authenticator apps and services that use them.

Demo

Live Demo at https://daplie.github.io/browser-authenticator/

You may also be interested in Node Authenticator over at https://github.com/Daplie/node-authenticator

Usage

bower install authenticator --save
'use strict';

var authenticator = window.Authenticator;

authenticator.generateKey().then(function (formattedKey) {
  // "acqo ua72 d3yf a4e5 uorx ztkh j2xl 3wiz"

  authenticator.generateTotpUri(formattedKey, "john.doe@email.com", "ACME Co", 'SHA1', 6, 30);
  //
  // otpauth://totp/ACME%20Co:john.doe@email.com?secret=ACQOUA72D3YFA4E5UORXZTKHJ2XL3WIZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30


  authenticator.generateToken(formattedKey).then(function (formattedToken) {
    // "957 124"

    authenticator.verifyToken(formattedKey, formattedToken).then(function (result) {
      // { delta: 0 }
    });

    authenticator.verifyToken(formattedKey, '000 000').then(function (result) {
      // null
    });
  });
});

Browsers that support WebCrypto

In total there are only a few hundred lines of uncompressed code here.

Each file is very small.

<script src="bower_components/unibabel/index.js"></script>
<script src="bower_components/unibabel/unibabel.hex.js"></script>
<script src="bower_components/unibabel/unibabel.base32.js"></script>

<script src="bower_components/botp/sha1-hmac.js"></script>
<script src="bower_components/botp/index.js"></script>

<script src="bower_components/authenticator/authenticator.js"></script>

Browsers that do not support WebCrypto

<!-- forge.hmac -->
<script src="bower_components/forge/js/util.js"></script>
<script src="bower_components/forge/js/sha1.js"></script>
<script src="bower_components/forge/js/hmac.js"></script>

<!-- forge.random.getBytes -->
<script src="bower_components/forge/js/sha256.js"></script>
<script src="bower_components/forge/js/cipher.js"></script>
<script src="bower_components/forge/js/cipherModes.js"></script>
<script src="bower_components/forge/js/aes.js"></script>
<script src="bower_components/forge/js/prng.js"></script>
<script src="bower_components/forge/js/random.js"></script>

TODO: I'd love some help pruning the important bits out of the forge code. Just using an alternate CPRNG would be very helpful in trimming the fat.

API

generateKey()

generates a 32-character (160-bit) base32 key

generateToken(formattedKey)

generates a 6-digit (20-bit) decimal time-based token

verifyToken(formattedKey, formattedToken)

validates a time-based token within a +/- 30 second (90 seconds) window

returns null on failure or an object such as { delta: 0 } on success

generateTotpUri(formattedKey, accountName, issuer, algorithm, digits, period)

generates an OTPAUTH:// scheme URI for QR Code generation.

OTPAuth Scheme

Note that ISSUER is specified twice for backwards / forwards compatibility.

QR Code

See https://davidshimjs.github.io/qrcodejs/ and https://github.com/soldair/node-qrcode.

Example use with qrcode.js in the browser:

'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).