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 |
|
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 |
|
45 |
|
46 | ## About
|
47 |
|
48 | `otplib` is a JavaScript One Time Password (OTP) library for OTP generation and verification.
|
49 |
|
50 | It implements both [HOTP][rfc-4226-wiki] - [RFC 4226][rfc-4226]
|
51 | and [TOTP][rfc-6238-wiki] - [RFC 6238][rfc-6238],
|
52 | and are tested against the test vectors provided in their respective RFC specifications.
|
53 | These 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 |
|
58 | This library is also compatible with [Google Authenticator](https://github.com/google/google-authenticator),
|
59 | and 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
|
87 | npm install otplib --save
|
88 | ```
|
89 |
|
90 | ```js
|
91 | import { authenticator } from 'otplib';
|
92 |
|
93 | const secret = 'KVKFKRCPNZQUYMLXOVYDSQKJKZDTSRLD';
|
94 | // Alternative:
|
95 | // const secret = authenticator.generateSecret();
|
96 | // Note: .generateSecret() is only available for authenticator and not totp/hotp
|
97 |
|
98 | const token = authenticator.generate(secret);
|
99 |
|
100 | try {
|
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 |
|
112 | Please replace "authenticator" with "totp" or "hotp" depending on your requirements.
|
113 |
|
114 | ```js
|
115 | // For TOTP
|
116 | import { totp } from 'otplib';
|
117 | const token = totp.generate(secret);
|
118 | const isValid = totp.check(token, secret);
|
119 | const isValid = totp.verify({ token, secret });
|
120 |
|
121 | // For HOTP
|
122 | import { hotp } from 'otplib';
|
123 | const token = hotp.generate(secret, counter);
|
124 | const isValid = hotp.check(token, secret, counter);
|
125 | const isValid = hotp.verify({ token, secret, counter });
|
126 | ```
|
127 |
|
128 | For all available APIs, please refer to [API Documentation][project-v-api].
|
129 |
|
130 | ### In Browser
|
131 |
|
132 | The browser preset is a self-contained `umd` module, and it is provided in a separate bundle.
|
133 |
|
134 | ```bash
|
135 | npm install @otplib/preset-browser --save
|
136 | ```
|
137 |
|
138 | The 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 |
|
151 | For 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 |
|
165 | This library follows `semver`. As such, major version bumps usually mean API changes or behavior changes.
|
166 | Please check [upgrade notes](https://github.com/yeojz/otplib/wiki/upgrade-notes) for more information,
|
167 | especially before making any major upgrades.
|
168 |
|
169 | To simplify releases, all packages within this repository have their versions synced.
|
170 | Therefore, if there are any releases or updates to a package, we will bump all packages.
|
171 |
|
172 | Check out the release notes associated with each tagged versions
|
173 | in 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
|
187 | import { authenticator } from 'otplib'; // v11.x
|
188 | // to
|
189 | import { 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 |
|
197 | All instantiated classes will have their options inherited from their respective options
|
198 | generator. i.e. HOTP from `hotpOptions`, TOTP from `totpOptions`
|
199 | and Authenticator from `authenticatorOptions`.
|
200 |
|
201 | All OTP classes have an object setter and getter method to override these default options.
|
202 |
|
203 | For example,
|
204 |
|
205 | ```js
|
206 | import { authenticator, totp, hotp } from 'otplib';
|
207 |
|
208 | // setting
|
209 | authenticator.options = { digits: 6 };
|
210 | totp.options = { digits: 6 };
|
211 | hotp.options = { digits: 6 };
|
212 |
|
213 | // getting
|
214 | const opts = authenticator.options;
|
215 | const opts = totp.options;
|
216 | const opts = hotp.options;
|
217 |
|
218 | // reset to default
|
219 | authenticator.resetOptions();
|
220 | totp.resetOptions();
|
221 | hotp.resetOptions();
|
222 |
|
223 | // getting all options, with validation
|
224 | // and backfilled with library defaults
|
225 | const opts = authenticator.allOptions();
|
226 | const opts = totp.allOptions();
|
227 | const 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 |
|
300 | As 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 |
|
306 | This was added as some libraries like [expo.io][link-expo-crypto] or even
|
307 | the browser API ([window.Crypto.subtle][link-mdn-subtlecrypto]) started providing
|
308 | only async methods.
|
309 |
|
310 | You 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 |
|
316 | The approximate size for the **optimised, minified + gzipped** bundle is **9.53KB**.
|
317 | Paired with the gzipped browser `buffer.js` module, it would be about `7.65KB + 9.53KB = 17.18KB`.
|
318 |
|
319 | For more details, please refer to the [@otplib/preset-browser documentation][docs-preset-browser].
|
320 |
|
321 | ### Length of Secrets
|
322 |
|
323 | In [RFC 6238][rfc-6238], the secret / seed length for different algorithms are predefined:
|
324 |
|
325 | ```txt
|
326 | HMAC-SHA1 - 20 bytes
|
327 | HMAC-SHA256 - 32 bytes
|
328 | HMAC-SHA512 - 64 bytes
|
329 | ```
|
330 |
|
331 | As such, the length of the secret provided (after any decoding) will be padded and sliced
|
332 | according to the expected length for respective algorithms.
|
333 |
|
334 | ### Google Authenticator
|
335 |
|
336 | #### Difference between Authenticator and TOTP
|
337 |
|
338 | The 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 |
|
345 | Google Authenticator requires keys to be base32 encoded.
|
346 | It also requires the base32 encoder to be [RFC 3548][rfc-3548] compliant.
|
347 |
|
348 | OTP calculation will still work should you want to use
|
349 | other base32 encoding methods (like Crockford's Base32)
|
350 | but it will NOT be compatible with Google Authenticator.
|
351 |
|
352 | ```js
|
353 | const secret = authenticator.generateSecret(); // base32 encoded hex secret key
|
354 | const token = authenticator.generate(secret);
|
355 | ```
|
356 |
|
357 | #### Displaying a QR code
|
358 |
|
359 | You may want to generate and display a QR Code so that users can scan
|
360 | instead of manually entering the secret. Google Authenticator and similar apps
|
361 | take in a QR code that holds a URL with the protocol `otpauth://`,
|
362 | which you get from `authenticator.keyuri`.
|
363 |
|
364 | Google Authenticator will ignore the `algorithm`, `digits`, and `step` options.
|
365 | See the [keyuri documentation](https://github.com/google/google-authenticator/wiki/Key-Uri-Format)
|
366 | for more information.
|
367 |
|
368 | If you are using a different authenticator app, check the documentation
|
369 | for that app to see if any options are ignored, which will result in invalid tokens.
|
370 |
|
371 | While this library provides the "otpauth" uri, you'll need a library to
|
372 | generate the QR Code image.
|
373 |
|
374 | An example is shown below:
|
375 |
|
376 | ```js
|
377 | // npm install qrcode
|
378 | import qrcode from 'qrcode';
|
379 | import { authenticator } from '@otplib/preset-default';
|
380 |
|
381 | const user = 'A user name, possibly an email';
|
382 | const service = 'A service name';
|
383 |
|
384 | // v11.x and above
|
385 | const otpauth = authenticator.keyuri(user, service, secret);
|
386 |
|
387 | // v10.x and below
|
388 | const otpauth = authenticator.keyuri(
|
389 | encodeURIComponent(user),
|
390 | encodeURIComponent(service),
|
391 | secret
|
392 | );
|
393 |
|
394 | qrcode.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 |
|
408 | Helper methods for getting the remaining time and used time within a validity period
|
409 | of a `totp` or `authenticator` token were introduced in `v10.0.0`.
|
410 |
|
411 | ```js
|
412 | authenticator.timeUsed(); // or totp.timeUsed();
|
413 | authenticator.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.
|
423 | While `otplib` does not provide an `expo` specified package, with the re-architecture
|
424 | of `otplib`, you can now provide an expo native `createDigest` to the library.
|
425 |
|
426 | Alternatively, you can make use of crypto provided by `@otplib/plugin-crypto-js` or
|
427 | the bundled browser umd module `@otplib/preset-browser`.
|
428 |
|
429 | Pull Requests are much welcomed for a native expo implementation as well.
|
430 |
|
431 | ### Exploring with local-repl
|
432 |
|
433 | If you'll like to explore the library with `local-repl` you can do so as well.
|
434 |
|
435 | ```bash
|
436 | # after cloning the repo:
|
437 | npm run setup
|
438 | npm run build
|
439 | npx 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 |
|
452 | It is common for services to also provide a set of backup codes to authenticate
|
453 | and bypass the OTP step in the event that you are not able to access your 2FA
|
454 | device or have misplaced the device.
|
455 |
|
456 | As this process is separate from the specifications for OTP, this library does not
|
457 | provide any backup code related verification logic, and thus would have to be
|
458 | implemented separately.
|
459 |
|
460 | ## Contributors
|
461 |
|
462 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
463 |
|
464 |
|
465 |
|
466 |
|
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 |
|
479 |
|
480 |
|
481 |
|
482 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors)
|
483 | specification. 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 |
|
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 |
|
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 |
|
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
|