UNPKG

22.4 kBMarkdownView Raw
1# otplib
2
3> Time-based (TOTP) and HMAC-based (HOTP) One-Time Password library
4
5[![npm][badge-npm]][project-npm]
6[![Build Status][badge-circle]][project-circle]
7[![Coverage Status][badge-coveralls]][project-coveralls]
8[![npm downloads][badge-npm-downloads]][project-npm]
9[![TypeScript Support][badge-type-ts]][project-v-api]
10
11---
12
13<!-- TOC depthFrom:2 -->
14
15- [About](#about)
16- [Features](#features)
17- [Quick Start](#quick-start)
18 - [In Node.js](#in-nodejs)
19 - [In Browser](#in-browser)
20- [References](#references)
21 - [API / Demo Website](#api--demo-website)
22 - [Versioning](#versioning)
23 - [Migrating from v11.x](#migrating-from-v11x)
24 - [Available Options](#available-options)
25 - [HOTP Options](#hotp-options)
26 - [TOTP Options](#totp-options)
27 - [Authenticator Options](#authenticator-options)
28- [Appendix](#appendix)
29 - [Type Definitions](#type-definitions)
30 - [Async Support](#async-support)
31 - [Browser Compatiblity](#browser-compatiblity)
32 - [Length of Secrets](#length-of-secrets)
33 - [Google Authenticator](#google-authenticator)
34 - [Difference between Authenticator and TOTP](#difference-between-authenticator-and-totp)
35 - [RFC3548 Base32](#rfc3548-base32)
36 - [Displaying a QR code](#displaying-a-qr-code)
37 - [Getting Time Remaining / Time Used](#getting-time-remaining--time-used)
38 - [Using with Expo](#using-with-expo)
39 - [Exploring with local-repl](#exploring-with-local-repl)
40 - [OTP Backup Codes](#otp-backup-codes)
41- [Contributors](#contributors)
42- [License](#license)
43
44<!-- /TOC -->
45
46## About
47
48`otplib` is a JavaScript One Time Password (OTP) library for OTP generation and verification.
49
50It implements both [HOTP][rfc-4226-wiki] - [RFC 4226][rfc-4226]
51and [TOTP][rfc-6238-wiki] - [RFC 6238][rfc-6238],
52and are tested against the test vectors provided in their respective RFC specifications.
53These datasets can be found in the `tests/data` folder.
54
55- [RFC 4226 Dataset][rfc-4226-dataset]
56- [RFC 6238 Dataset][rfc-6238-dataset]
57
58This library is also compatible with [Google Authenticator](https://github.com/google/google-authenticator),
59and includes additional methods to allow you to work with Google Authenticator.
60
61## Features
62
63- Typescript support
64- [Class][link-mdn-classes] interfaces
65- [Function][link-mdn-functions] interfaces
66- [Async][link-mdn-async] interfaces
67- Pluggable modules (crypto / base32)
68 - `crypto (node)`
69 - `crypto-js`
70 - `@ronomon/crypto-async`
71 - `thirty-two`
72 - `base32-encode` + `base32-decode`
73- Presets provided
74 - `browser`
75 - `default (node)`
76 - `default-async (same as default, but with async methods)`
77 - `v11 (adapter for previous version)`
78
79## Quick Start
80
81> If you need to customise your base32 or crypto libraries,
82> check out the [In-Depth Guide][docs-in-depth] and [Available Packages][docs-available-packages]
83
84### In Node.js
85
86```bash
87npm install otplib --save
88```
89
90```js
91import { authenticator } from 'otplib';
92
93const secret = 'KVKFKRCPNZQUYMLXOVYDSQKJKZDTSRLD';
94// Alternative:
95// const secret = authenticator.generateSecret();
96// Note: .generateSecret() is only available for authenticator and not totp/hotp
97
98const token = authenticator.generate(secret);
99
100try {
101 const isValid = authenticator.check(token, secret);
102 // or
103 const isValid = authenticator.verify({ token, secret });
104} catch (err) {
105 // Possible errors
106 // - options validation
107 // - "Invalid input - it is not base32 encoded string" (if thiry-two is used)
108 console.error(err);
109}
110```
111
112Please replace "authenticator" with "totp" or "hotp" depending on your requirements.
113
114```js
115// For TOTP
116import { totp } from 'otplib';
117const token = totp.generate(secret);
118const isValid = totp.check(token, secret);
119const isValid = totp.verify({ token, secret });
120
121// For HOTP
122import { hotp } from 'otplib';
123const token = hotp.generate(secret, counter);
124const isValid = hotp.check(token, secret, counter);
125const isValid = hotp.verify({ token, secret, counter });
126```
127
128For all available APIs, please refer to [API Documentation][project-v-api].
129
130### In Browser
131
132The browser preset is a self-contained `umd` module, and it is provided in a separate bundle.
133
134```bash
135npm install @otplib/preset-browser --save
136```
137
138The following is an example, where we are using the scripts hosted by `unpkg.com`.
139
140```html
141<script src="https://unpkg.com/@otplib/preset-browser@^12.0.0/buffer.js"></script>
142<script src="https://unpkg.com/@otplib/preset-browser@^12.0.0/index.js"></script>
143
144<script type="text/javascript">
145 // window.otplib.authenticator
146 // window.otplib.hotp
147 // window.otplib.totp
148</script>
149```
150
151For more details, please refer to the [@otplib/preset-browser documentation][docs-preset-browser].
152
153## References
154
155### API / Demo Website
156
157| Version | Links |
158| --------------- | ----------------------------------------------------------------------------------- |
159| v12.x | [Website][project-v-site] / [API][project-v-api] / [Readme][project-v-readme] |
160| v11.x | [API][project-v11-api] / [Readme][project-v11-readme] |
161| v10.x and below | Available via git history |
162
163### Versioning
164
165This library follows `semver`. As such, major version bumps usually mean API changes or behavior changes.
166Please check [upgrade notes](https://github.com/yeojz/otplib/wiki/upgrade-notes) for more information,
167especially before making any major upgrades.
168
169To simplify releases, all packages within this repository have their versions synced.
170Therefore, if there are any releases or updates to a package, we will bump all packages.
171
172Check out the release notes associated with each tagged versions
173in the [releases](https://github.com/yeojz/otplib/releases) page.
174
175| Release Type | Version Pattern | Command | |
176| :---------------- | --------------- | ------------------------- | :------------------------------------ |
177| Current / Stable | 0.0.0 | `npm install otplib` | [![npm][badge-npm]][project-npm] |
178| Release Candidate | 0.0.0-0 | `npm install otplib@next` | [![npm][badge-npm-next]][project-npm] |
179
180### Migrating from v11.x
181
182> v12.x is a huge architectural and language rewrite. Please check out the docs if you are migrating.
183> A preset adapter is available to provide methods that behave like `v11.x` of `otplib`.
184
185```js
186// Update
187import { authenticator } from 'otplib'; // v11.x
188// to
189import { authenticator } from '@otplib/preset-v11';
190
191// There should be no changes to your current code.
192// However, deprecated or modified class methods will have console.warn.
193```
194
195### Available Options
196
197All instantiated classes will have their options inherited from their respective options
198generator. i.e. HOTP from `hotpOptions`, TOTP from `totpOptions`
199and Authenticator from `authenticatorOptions`.
200
201All OTP classes have an object setter and getter method to override these default options.
202
203For example,
204
205```js
206import { authenticator, totp, hotp } from 'otplib';
207
208// setting
209authenticator.options = { digits: 6 };
210totp.options = { digits: 6 };
211hotp.options = { digits: 6 };
212
213// getting
214const opts = authenticator.options;
215const opts = totp.options;
216const opts = hotp.options;
217
218// reset to default
219authenticator.resetOptions();
220totp.resetOptions();
221hotp.resetOptions();
222
223// getting all options, with validation
224// and backfilled with library defaults
225const opts = authenticator.allOptions();
226const opts = totp.allOptions();
227const opts = hotp.allOptions();
228```
229
230#### HOTP Options
231
232| Option | Type | Description |
233| ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
234| algorithm | string | The algorithm used for calculating the HMAC. |
235| createDigest | function | Creates the digest which token is derived from. |
236| createHmacKey | function | Formats the secret into a HMAC key, applying transformations (like padding) where needed. |
237| digest | string | **USE WITH CAUTION**. Same digest = same token. <br />Used in cases where digest is generated externally. (eg: async use cases) |
238| digits | integer | The length of the token. |
239| encoding | string | The encoding that was used on the secret. |
240
241```js
242// HOTP defaults
243{
244 algorithm: 'sha1'
245 createDigest: undefined, // to be provided via a @otplib/plugin-*
246 createHmacKey: hotpCreateHmacKey,
247 digits: 6,
248 encoding: 'ascii',
249}
250```
251
252#### TOTP Options
253
254> Note: Includes all HOTP Options
255
256| Option | Type | Description |
257| ------ | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
258| epoch | integer | **USE WITH CAUTION**. Same epoch = same token. <br />Starting time since the UNIX epoch (seconds). <br /> Epoch is JavaScript formatted. i.e. `Date.now()` or `UNIX time * 1000` |
259| step | integer | Time step (seconds) |
260| window | integer, <br /> [number, number] | Tokens in the previous and future x-windows that should be considered valid. <br /> If integer, same value will be used for both. <br /> Alternatively, define array: `[past, future]` |
261
262```js
263// TOTP defaults
264{
265 // ...includes all HOTP defaults
266 createHmacKey: totpCreateHmacKey,
267 epoch: Date.now(),
268 step: 30,
269 window: 0,
270}
271```
272
273#### Authenticator Options
274
275> Note: Includes all HOTP + TOTP Options
276
277| Option | Type | Description |
278| ----------------- | -------- | ----------------------------------------------------------------------------------------------------- |
279| createRandomBytes | function | Creates a random string containing the defined number of bytes to be used in generating a secret key. |
280| keyEncoder | function | Encodes a secret key into a Base32 string before it is sent to the user (in QR Code etc). |
281| keyDecoder | function | Decodes the Base32 string given by the user into a secret. |
282
283```js
284// Authenticator defaults
285{
286 // ...includes all HOTP + TOTP defaults
287 encoding: 'hex',
288 createRandomBytes: undefined, // to be provided via a @otplib/plugin-*
289 keyEncoder: undefined, // to be provided via a @otplib/plugin-*
290 keyDecoder: undefined, // to be provided via a @otplib/plugin-*
291}
292```
293
294## Appendix
295
296### Type Definitions
297
298`TypeScript` support was introduced in `v10.0.0`, which added type definitions over `.js` files.
299
300As of `v12.0.0`, the library has been re-written in Typescript from the ground up.
301
302### Async Support
303
304`async` support was introduced in `v12.0.0` as an additional core library.
305
306This was added as some libraries like [expo.io][link-expo-crypto] or even
307the browser API ([window.Crypto.subtle][link-mdn-subtlecrypto]) started providing
308only async methods.
309
310You to find more details in the [core-async][docs-core-async] folder.
311
312### Browser Compatiblity
313
314`@otplib/preset-browser` is a `umd` bundle with some node modules replaced to reduce the browser size.
315
316The approximate size for the **optimised, minified + gzipped** bundle is **9.53KB**.
317Paired with the gzipped browser `buffer.js` module, it would be about `7.65KB + 9.53KB = 17.18KB`.
318
319For more details, please refer to the [@otplib/preset-browser documentation][docs-preset-browser].
320
321### Length of Secrets
322
323In [RFC 6238][rfc-6238], the secret / seed length for different algorithms are predefined:
324
325```txt
326HMAC-SHA1 - 20 bytes
327HMAC-SHA256 - 32 bytes
328HMAC-SHA512 - 64 bytes
329```
330
331As such, the length of the secret provided (after any decoding) will be padded and sliced
332according to the expected length for respective algorithms.
333
334### Google Authenticator
335
336#### Difference between Authenticator and TOTP
337
338The default encoding option has been set to `hex` (Authenticator) instead of `ascii` (TOTP).
339
340#### RFC3548 Base32
341
342> Note: [RFC4648][rfc-4648] obseletes [RFC 3548][rfc-3548].
343> Any encoders following the newer specifications will work.
344
345Google Authenticator requires keys to be base32 encoded.
346It also requires the base32 encoder to be [RFC 3548][rfc-3548] compliant.
347
348OTP calculation will still work should you want to use
349other base32 encoding methods (like Crockford's Base32)
350but it will NOT be compatible with Google Authenticator.
351
352```js
353const secret = authenticator.generateSecret(); // base32 encoded hex secret key
354const token = authenticator.generate(secret);
355```
356
357#### Displaying a QR code
358
359You may want to generate and display a QR Code so that users can scan
360instead of manually entering the secret. Google Authenticator and similar apps
361take in a QR code that holds a URL with the protocol `otpauth://`,
362which you get from `authenticator.keyuri`.
363
364Google Authenticator will ignore the `algorithm`, `digits`, and `step` options.
365See the [keyuri documentation](https://github.com/google/google-authenticator/wiki/Key-Uri-Format)
366for more information.
367
368If you are using a different authenticator app, check the documentation
369for that app to see if any options are ignored, which will result in invalid tokens.
370
371While this library provides the "otpauth" uri, you'll need a library to
372generate the QR Code image.
373
374An example is shown below:
375
376```js
377// npm install qrcode
378import qrcode from 'qrcode';
379import { authenticator } from '@otplib/preset-default';
380
381const user = 'A user name, possibly an email';
382const service = 'A service name';
383
384// v11.x and above
385const otpauth = authenticator.keyuri(user, service, secret);
386
387// v10.x and below
388const otpauth = authenticator.keyuri(
389 encodeURIComponent(user),
390 encodeURIComponent(service),
391 secret
392);
393
394qrcode.toDataURL(otpauth, (err, imageUrl) => {
395 if (err) {
396 console.log('Error with QR');
397 return;
398 }
399 console.log(imageUrl);
400});
401```
402
403> **Note**: For versions `v10.x` and below, `keyuri` does not URI encode
404> `user` and `service`. You'll need to do so before passing in the parameteres.
405
406### Getting Time Remaining / Time Used
407
408Helper methods for getting the remaining time and used time within a validity period
409of a `totp` or `authenticator` token were introduced in `v10.0.0`.
410
411```js
412authenticator.timeUsed(); // or totp.timeUsed();
413authenticator.timeRemaining(); // or totp.timeRemaining();
414
415// The start of a new token would be when:
416// - timeUsed() === 0
417// - timeRemaining() === step
418```
419
420### Using with Expo
421
422[Expo][link-expo-io] contains modified crypto implmentations targeted at the platform.
423While `otplib` does not provide an `expo` specified package, with the re-architecture
424of `otplib`, you can now provide an expo native `createDigest` to the library.
425
426Alternatively, you can make use of crypto provided by `@otplib/plugin-crypto-js` or
427the bundled browser umd module `@otplib/preset-browser`.
428
429Pull Requests are much welcomed for a native expo implementation as well.
430
431### Exploring with local-repl
432
433If you'll like to explore the library with `local-repl` you can do so as well.
434
435```bash
436# after cloning the repo:
437npm run setup
438npm run build
439npx local-repl
440# You should see something like:
441# Node v8.9.4, local-repl 4.0.0
442# otplib 10.0.0
443# Context: otplib
444# [otplib] >
445
446[otplib] > secret = 'KVKFKRCPNZQUYMLXOVYDSQKJKZDTSRLD'
447[otplib] > otplib.authenticator.generate(secret)
448```
449
450### OTP Backup Codes
451
452It is common for services to also provide a set of backup codes to authenticate
453and bypass the OTP step in the event that you are not able to access your 2FA
454device or have misplaced the device.
455
456As this process is separate from the specifications for OTP, this library does not
457provide any backup code related verification logic, and thus would have to be
458implemented separately.
459
460## Contributors
461
462Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
463
464<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
465<!-- prettier-ignore-start -->
466<!-- markdownlint-disable -->
467<table>
468 <tr>
469 <td align="center"><a href="https://github.com/yeojz"><img src="https://avatars2.githubusercontent.com/u/429598?v=4" width="80px;" alt="Gerald Yeo"/><br /><sub><b>Gerald Yeo</b></sub></a><br /><a href="https://github.com/yeojz/otplib/commits?author=yeojz" title="Code">πŸ’»</a> <a href="https://github.com/yeojz/otplib/commits?author=yeojz" title="Documentation">πŸ“–</a> <a href="#maintenance-yeojz" title="Maintenance">🚧</a> <a href="https://github.com/yeojz/otplib/commits?author=yeojz" title="Tests">⚠️</a></td>
470 <td align="center"><a href="https://ols.io"><img src="https://avatars3.githubusercontent.com/u/6209178?v=4" width="80px;" alt="Oliver Schneider"/><br /><sub><b>Oliver Schneider</b></sub></a><br /><a href="https://github.com/yeojz/otplib/commits?author=olsio" title="Documentation">πŸ“–</a></td>
471 <td align="center"><a href="https://developer.mozilla.org/profiles/madarche/"><img src="https://avatars0.githubusercontent.com/u/152407?v=4" width="80px;" alt="Marc-AurΓ¨le DARCHE"/><br /><sub><b>Marc-AurΓ¨le DARCHE</b></sub></a><br /><a href="https://github.com/yeojz/otplib/commits?author=madarche" title="Documentation">πŸ“–</a></td>
472 <td align="center"><a href="http://shakram02.github.io/"><img src="https://avatars3.githubusercontent.com/u/10996982?v=4" width="80px;" alt="Ahmed Hamdy (@shakram02)"/><br /><sub><b>Ahmed Hamdy (@shakram02)</b></sub></a><br /><a href="https://github.com/yeojz/otplib/commits?author=shakram02" title="Documentation">πŸ“–</a></td>
473 <td align="center"><a href="https://tony.brix.ninja"><img src="https://avatars3.githubusercontent.com/u/97994?v=4" width="80px;" alt="Tony Brix"/><br /><sub><b>Tony Brix</b></sub></a><br /><a href="https://github.com/yeojz/otplib/commits?author=UziTech" title="Code">πŸ’»</a> <a href="https://github.com/yeojz/otplib/commits?author=UziTech" title="Documentation">πŸ“–</a></td>
474 <td align="center"><a href="https://github.com/encX"><img src="https://avatars3.githubusercontent.com/u/5965883?v=4" width="80px;" alt="Plai"/><br /><sub><b>Plai</b></sub></a><br /><a href="https://github.com/yeojz/otplib/commits?author=encX" title="Documentation">πŸ“–</a></td>
475 </tr>
476</table>
477
478<!-- markdownlint-enable -->
479<!-- prettier-ignore-end -->
480<!-- ALL-CONTRIBUTORS-LIST:END -->
481
482This project follows the [all-contributors](https://github.com/all-contributors/all-contributors)
483specification. Contributions of any kind welcome!
484
485## License
486
487`otplib` is [MIT licensed][project-license]
488
489<img width="150" src="https://otplib.yeojz.dev/otplib.png" />
490
491<!-- Badges -->
492
493[badge-circle]: https://img.shields.io/circleci/project/github/yeojz/otplib/master.svg?style=flat-square
494[badge-coveralls]: https://img.shields.io/coveralls/yeojz/otplib/master.svg?style=flat-square
495[badge-npm-downloads]: https://img.shields.io/npm/dt/otplib.svg?style=flat-square
496[badge-npm-next]: https://img.shields.io/npm/v/otplib/next.svg?style=flat-square
497[badge-npm]: https://img.shields.io/npm/v/otplib.svg?style=flat-square
498[badge-type-ts]: https://img.shields.io/badge/typedef-.d.ts-blue.svg?style=flat-square&longCache=true
499
500<!-- External Links -->
501
502[link-expo-crypto]: https://docs.expo.io/versions/v33.0.0/sdk/crypto/
503[link-expo-io]: https://expo.io
504[link-mdn-async]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
505[link-mdn-classes]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
506[link-mdn-functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
507[link-mdn-subtlecrypto]: https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto
508[link-npm-buffer]: https://www.npmjs.com/package/buffer
509[rfc-3548]: http://tools.ietf.org/html/rfc3548
510[rfc-4226-dataset]: https://github.com/yeojz/otplib/blob/master/tests/data/rfc-4226.ts
511[rfc-4226-wiki]: http://en.wikipedia.org/wiki/HMAC-based_One-time_Password_Algorithm
512[rfc-4226]: http://tools.ietf.org/html/rfc4226
513[rfc-4648]: https://tools.ietf.org/html/rfc4648
514[rfc-6238-dataset]: https://github.com/yeojz/otplib/blob/master/tests/data/rfc-6238.ts
515[rfc-6238-wiki]: http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm
516[rfc-6238]: http://tools.ietf.org/html/rfc6238
517
518<!-- Project Links -->
519
520[docs-available-packages]: https://github.com/yeojz/otplib/blob/master/packages/README.md
521[docs-core-async]: https://github.com/yeojz/otplib/blob/master/packages/otplib-core-async/README.md
522[docs-in-depth]: https://github.com/yeojz/otplib/blob/master/packages/otplib-core/README.md#getting-started
523[docs-preset-browser-src]: https://github.com/yeojz/otplib/blob/master/packages/otplib-preset-browser/src/index.ts
524[docs-preset-browser]: https://github.com/yeojz/otplib/blob/master/packages/otplib-preset-browser/README.md
525[project-circle]: https://circleci.com/gh/yeojz/otplib
526[project-coveralls]: https://coveralls.io/github/yeojz/otplib
527[project-license]: https://github.com/yeojz/otplib/blob/master/LICENSE
528[project-npm]: https://www.npmjs.com/package/otplib
529[project-repo]: https://github.com/yeojz/otplib
530[project-v-api]: https://otplib.yeojz.dev/api
531[project-v-readme]: https://github.com/yeojz/otplib/blob/master/README.md
532[project-v-site]: https://otplib.yeojz.dev
533[project-v11-api]: https://5d4d0cc4c85e00000788a456--otplib.netlify.com/docs
534[project-v11-readme]: https://github.com/yeojz/otplib/blob/d0aedccbca8ae7ec1983f40da4d7a14c9e815e9c/README.md