| 
									
										
										
										
											2015-10-07 12:56:17 -07:00
										 |  |  | Node.js Authenticator | 
					
						
							|  |  |  | ===================== | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-28 00:30:33 -06:00
										 |  |  | | Sponsored by [ppl](https://ppl.family) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-07 12:56:17 -07:00
										 |  |  | Two- and Multi- Factor Authenication (2FA / MFA) for node.js | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-22 19:07:47 -07:00
										 |  |  |  | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-07 12:56:17 -07:00
										 |  |  | There are a number of apps that various websites use to give you 6-digit codes to increase security when you log in: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-22 19:15:13 -07:00
										 |  |  | * Authy (shown above) [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) | 
					
						
							| 
									
										
										
										
											2015-10-07 12:56:17 -07:00
										 |  |  | * 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), | 
					
						
							| 
									
										
										
										
											2015-10-07 13:39:16 -07:00
										 |  |  | including Google, Microsoft, Facebook, and Digital Ocean for starters. | 
					
						
							| 
									
										
										
										
											2015-10-07 12:56:17 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 01:13:22 -08:00
										 |  |  | Browser & Commandline Authenticator | 
					
						
							| 
									
										
										
										
											2015-10-23 01:15:32 -07:00
										 |  |  | --------------------- | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 01:13:22 -08:00
										 |  |  | You may also be interested in | 
					
						
							| 
									
										
										
										
											2015-10-23 01:15:32 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-28 00:30:33 -06:00
										 |  |  | * [Browser Authenticator](https://git.coolaj86.com/coolaj86/browser-authenticator) over at <https://git.coolaj86.com/coolaj86/browser-authenticator> | 
					
						
							|  |  |  | * [Commandline Authenticator](https://git.coolaj86.com/coolaj86/authenticator-cli) over at <https://git.coolaj86.com/coolaj86/authenticator-cli> | 
					
						
							| 
									
										
										
										
											2016-01-02 01:13:22 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | Install | 
					
						
							| 
									
										
										
										
											2015-10-07 12:56:17 -07:00
										 |  |  | ===== | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 01:13:22 -08:00
										 |  |  | **node.js api** | 
					
						
							| 
									
										
										
										
											2015-10-07 12:56:17 -07:00
										 |  |  | ```bash | 
					
						
							|  |  |  | npm install authenticator --save | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 01:13:22 -08:00
										 |  |  | **command line** | 
					
						
							|  |  |  | ```bash | 
					
						
							|  |  |  | npm install authenticator-cli --global | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Usage | 
					
						
							|  |  |  | ===== | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | **node.js api** | 
					
						
							| 
									
										
										
										
											2015-10-07 12:56:17 -07:00
										 |  |  | ```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 | 
					
						
							| 
									
										
										
										
											2015-10-24 11:38:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-03 00:16:18 -08:00
										 |  |  | authenticator.generateTotpUri(formattedKey, "john.doe@email.com", "ACME Co", 'SHA1', 6, 30); | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30 | 
					
						
							|  |  |  | ``` | 
					
						
							| 
									
										
										
										
											2015-10-24 11:38:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 01:13:22 -08:00
										 |  |  | **command line** | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | # see help
 | 
					
						
							|  |  |  | authenticator --help | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # generate a key and display qr code
 | 
					
						
							|  |  |  | authenticator --qr | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-24 11:38:31 -07:00
										 |  |  | API | 
					
						
							|  |  |  | --- | 
					
						
							| 
									
										
										
										
											2015-10-07 13:51:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 01:15:06 -08:00
										 |  |  | ```javascript | 
					
						
							|  |  |  | generateKey()                               // generates a 32-character (160-bit) base32 key | 
					
						
							| 
									
										
										
										
											2015-10-07 13:54:55 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 01:15:06 -08:00
										 |  |  | generateToken(formattedKey)                 // generates a 6-digit (20-bit) decimal time-based token | 
					
						
							| 
									
										
										
										
											2015-10-07 13:51:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 01:15:06 -08:00
										 |  |  | 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 | 
					
						
							| 
									
										
										
										
											2015-11-03 00:16:18 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 01:15:06 -08:00
										 |  |  |                                             // generates an `OTPAUTH://` scheme URI for QR Code generation. | 
					
						
							|  |  |  | generateTotpUri(formattedKey, accountName, issuer, algorithm, digits, period) | 
					
						
							|  |  |  | ``` | 
					
						
							| 
									
										
										
										
											2015-11-03 00:16:18 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | **OTPAuth Scheme** | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | * <https://github.com/google/google-authenticator/wiki/Key-Uri-Format> | 
					
						
							|  |  |  | * `otpauth://totp/<<ISSUER>>:<<ACCOUNT_NAME>>?secret=<<BASE32_KEY>>&issuer=<<ISSUER>>` | 
					
						
							|  |  |  | * `otpauth://totp/<<ISSUER>>:<<ACCOUNT_NAME>>?secret=<<BASE32_KEY>>&issuer=<<ISSUER>>&algorithm=<<ALGO>>&digits=<<INT>>&period=<<SECONDS>>` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Note that `ISSUER` is specified twice for backwards / forwards compatibility. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-07 13:39:16 -07:00
										 |  |  | QR Code | 
					
						
							|  |  |  | ------- | 
					
						
							| 
									
										
										
										
											2015-10-07 12:56:17 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-07 13:39:16 -07:00
										 |  |  | See <https://davidshimjs.github.io/qrcodejs/> and <https://github.com/soldair/node-qrcode>. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-22 19:12:52 -07:00
										 |  |  |  | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-07 13:39:16 -07:00
										 |  |  | Example use with `qrcode.js` in the browser: | 
					
						
							| 
									
										
										
										
											2015-10-07 12:56:17 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```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? | 
					
						
							|  |  |  | ------------------ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-23 12:06:45 -07:00
										 |  |  | It doesn't use native node crypto and there are open security issues which have been left unaddressed. |