# Browser Authenticator Two- / Multi- Factor Authenication (2FA / MFA) for browser JavaScript ![](https://blog.authy.com/assets/posts/authenticator.png) There are a number of apps that various websites use to give you 6-digit codes to increase security when you log in: * [Authy](https://www.authy.com/personal/) [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) * 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, and Digital Ocean for starters. This module uses [`botp`](https://github.com/Daplie/botp) 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. Demo ==== [Live Demo](https://daplie.github.io/browser-authenticator/) at You may also be interested in [Node Authenticator](https://github.com/Daplie/node-authenticator) over at Usage ===== ```bash bower install authenticator --save ``` ```javascript '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. ``` ``` ### Browsers that do not support WebCrypto ``` ``` 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** * * `otpauth://totp/<>:<>?secret=<>&issuer=<>` * `otpauth://totp/<>:<>?secret=<>&issuer=<>&algorithm=<>&digits=<>&period=<>` Note that `ISSUER` is specified twice for backwards / forwards compatibility. QR Code ------- See and . ![](http://cdn9.howtogeek.com/wp-content/uploads/2014/10/sshot-7-22.png) Example use with `qrcode.js` in the browser: ```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).