@otplib/v12-adapter 13.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gerald Yeo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # @otplib/v12-adapter
2
+
3
+ > Drop-in replacement adapter for migrating `otplib` from v12 to v13.
4
+
5
+ This adapter mimics the v12 synchronous API while using v13's plugins under the hood. However, some fundamental changes from v13 may carry over.
6
+
7
+ **Note:** This is intended as a temporary bridge to help you upgrade to v13 without rewriting your entire application immediately. We strongly recommend fully migrating to the new v13 API when possible.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @otplib/v12-adapter
13
+ pnpm add @otplib/v12-adapter
14
+ yarn add @otplib/v12-adapter
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ Simply update your imports from `otplib` to `@otplib/v12-adapter`.
20
+
21
+ ### Before (v12)
22
+
23
+ ```typescript
24
+ import { authenticator } from "otplib";
25
+
26
+ const secret = authenticator.generateSecret();
27
+ const token = authenticator.generate(secret);
28
+ const isValid = authenticator.verify({ token, secret });
29
+ ```
30
+
31
+ ### After (v13 with Adapter)
32
+
33
+ ```typescript
34
+ import { authenticator } from "@otplib/v12-adapter";
35
+
36
+ // API remains exactly the same
37
+ const secret = authenticator.generateSecret();
38
+ const token = authenticator.generate(secret);
39
+ const isValid = authenticator.verify({ token, secret });
40
+ ```
41
+
42
+ ## Limitations
43
+
44
+ - **Class/Instance API Only**: This adapter only exports the `authenticator`, `totp`, and `hotp` singleton instances and their classes. If you were importing specific utility functions directly from `otplib/core` or other internal paths in v12, those are not covered by this adapter.
45
+ - **Sync/Async**: While this adapter provides a synchronous-looking API (like v12), it uses v13's plugins under the hood. For standard Node.js usage with the default `crypto` module, this works seamlessly.
46
+
47
+ ## Migration Guide
48
+
49
+ For a full guide on migrating to v13, including the benefits of the new architecture and how to use the new features, please see the [Migration Guide](https://otplib.yeojz.dev/guide/migrating-v12-to-v13).
50
+
51
+ ## License
52
+
53
+ [MIT](./LICENSE) © 2026 Gerald Yeo
package/dist/index.cjs ADDED
@@ -0,0 +1,542 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Authenticator: () => Authenticator,
24
+ HOTP: () => HOTP,
25
+ HashAlgorithms: () => HashAlgorithms,
26
+ KeyEncodings: () => KeyEncodings,
27
+ NobleCryptoPlugin: () => import_plugin_crypto_noble4.NobleCryptoPlugin,
28
+ ScureBase32Plugin: () => import_plugin_base32_scure4.ScureBase32Plugin,
29
+ TOTP: () => TOTP,
30
+ authenticator: () => authenticator,
31
+ hotp: () => hotp,
32
+ hotpDigestToToken: () => hotpDigestToToken,
33
+ totp: () => totp
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+
37
+ // src/hotp.ts
38
+ var import_core = require("@otplib/core");
39
+ var import_hotp = require("@otplib/hotp");
40
+ var import_plugin_base32_scure = require("@otplib/plugin-base32-scure");
41
+ var import_plugin_crypto_noble = require("@otplib/plugin-crypto-noble");
42
+ var import_uri = require("@otplib/uri");
43
+
44
+ // src/types.ts
45
+ var HashAlgorithms = {
46
+ SHA1: "sha1",
47
+ SHA256: "sha256",
48
+ SHA512: "sha512"
49
+ };
50
+ var KeyEncodings = {
51
+ ASCII: "ascii",
52
+ HEX: "hex",
53
+ BASE32: "base32",
54
+ BASE64: "base64",
55
+ LATIN1: "latin1",
56
+ UTF8: "utf8"
57
+ };
58
+
59
+ // src/hotp.ts
60
+ var defaultCrypto = new import_plugin_crypto_noble.NobleCryptoPlugin();
61
+ var defaultBase32 = new import_plugin_base32_scure.ScureBase32Plugin();
62
+ function secretToBytes(secret, encoding) {
63
+ if (encoding === KeyEncodings.BASE32 || encoding === "base32") {
64
+ return defaultBase32.decode(secret);
65
+ }
66
+ if (encoding === KeyEncodings.HEX || encoding === "hex") {
67
+ return (0, import_core.hexToBytes)(secret.replace(/\s/g, ""));
68
+ }
69
+ return (0, import_core.stringToBytes)(secret);
70
+ }
71
+ function hotpDigestToToken(hexDigest, digits) {
72
+ const digestBytes = (0, import_core.hexToBytes)(hexDigest);
73
+ const truncated = (0, import_core.dynamicTruncate)(digestBytes);
74
+ return (0, import_core.truncateDigits)(truncated, digits);
75
+ }
76
+ var HOTP = class _HOTP {
77
+ /**
78
+ * Stored options that can be modified
79
+ */
80
+ _options = {};
81
+ /**
82
+ * Default options applied to all operations
83
+ */
84
+ _defaultOptions = {};
85
+ constructor(defaultOptions = {}) {
86
+ this._defaultOptions = { ...defaultOptions };
87
+ this._options = {};
88
+ }
89
+ /**
90
+ * Get current options (merged with defaults)
91
+ */
92
+ get options() {
93
+ return { ...this._defaultOptions, ...this._options };
94
+ }
95
+ /**
96
+ * Set options (replaces current options)
97
+ */
98
+ set options(value) {
99
+ this._options = { ...value };
100
+ }
101
+ /**
102
+ * Creates a new instance with the specified default options
103
+ */
104
+ create(defaultOptions = {}) {
105
+ return new _HOTP(defaultOptions);
106
+ }
107
+ /**
108
+ * Returns class options polyfilled with default values
109
+ */
110
+ allOptions() {
111
+ const merged = {
112
+ algorithm: HashAlgorithms.SHA1,
113
+ digits: 6,
114
+ encoding: KeyEncodings.ASCII,
115
+ crypto: defaultCrypto,
116
+ base32: defaultBase32,
117
+ ...this._defaultOptions,
118
+ ...this._options
119
+ };
120
+ return Object.freeze(merged);
121
+ }
122
+ /**
123
+ * Reset options to defaults
124
+ */
125
+ resetOptions() {
126
+ this._options = {};
127
+ return this;
128
+ }
129
+ /**
130
+ * Generate an HOTP token
131
+ */
132
+ generate(secret, counter) {
133
+ const opts = this.allOptions();
134
+ const secretBytes = secretToBytes(secret, opts.encoding);
135
+ return (0, import_hotp.generateSync)({
136
+ secret: secretBytes,
137
+ counter,
138
+ algorithm: opts.algorithm,
139
+ digits: opts.digits,
140
+ crypto: opts.crypto
141
+ });
142
+ }
143
+ /**
144
+ * Check if a token is valid for the given secret and counter
145
+ */
146
+ check(token, secret, counter) {
147
+ const opts = this.allOptions();
148
+ const secretBytes = secretToBytes(secret, opts.encoding);
149
+ try {
150
+ const result = (0, import_hotp.verifySync)({
151
+ secret: secretBytes,
152
+ token,
153
+ counter,
154
+ algorithm: opts.algorithm,
155
+ digits: opts.digits,
156
+ counterTolerance: 0,
157
+ crypto: opts.crypto
158
+ });
159
+ return result.valid;
160
+ } catch {
161
+ return false;
162
+ }
163
+ }
164
+ /**
165
+ * Verify a token (object-based API)
166
+ */
167
+ verify(opts) {
168
+ if (typeof opts !== "object") {
169
+ throw new Error("Expecting argument 0 of verify to be an object");
170
+ }
171
+ return this.check(opts.token, opts.secret, opts.counter);
172
+ }
173
+ /**
174
+ * Generate an otpauth:// URI for HOTP
175
+ */
176
+ keyuri(accountName, issuer, secret, counter) {
177
+ const opts = this.allOptions();
178
+ return (0, import_uri.generateHOTP)({
179
+ label: accountName,
180
+ issuer,
181
+ secret,
182
+ algorithm: opts.algorithm,
183
+ digits: opts.digits,
184
+ counter
185
+ });
186
+ }
187
+ };
188
+
189
+ // src/totp.ts
190
+ var import_plugin_base32_scure2 = require("@otplib/plugin-base32-scure");
191
+ var import_plugin_crypto_noble2 = require("@otplib/plugin-crypto-noble");
192
+ var import_totp = require("@otplib/totp");
193
+ var import_uri2 = require("@otplib/uri");
194
+ var defaultCrypto2 = new import_plugin_crypto_noble2.NobleCryptoPlugin();
195
+ var defaultBase322 = new import_plugin_base32_scure2.ScureBase32Plugin();
196
+ function parseWindow(window, step) {
197
+ if (window === void 0 || window === 0) {
198
+ return 0;
199
+ }
200
+ if (typeof window === "number") {
201
+ return window * step;
202
+ }
203
+ return [window[0] * step, window[1] * step];
204
+ }
205
+ var TOTP = class _TOTP extends HOTP {
206
+ constructor(defaultOptions = {}) {
207
+ super(defaultOptions);
208
+ }
209
+ /**
210
+ * Creates a new TOTP instance with the specified default options
211
+ */
212
+ create(defaultOptions = {}) {
213
+ return new _TOTP(defaultOptions);
214
+ }
215
+ /**
216
+ * Returns class options polyfilled with TOTP default values
217
+ */
218
+ allOptions() {
219
+ const merged = {
220
+ algorithm: HashAlgorithms.SHA1,
221
+ digits: 6,
222
+ encoding: KeyEncodings.ASCII,
223
+ epoch: Date.now(),
224
+ step: 30,
225
+ window: 0,
226
+ crypto: defaultCrypto2,
227
+ base32: defaultBase322,
228
+ ...this._defaultOptions,
229
+ ...this._options
230
+ };
231
+ return Object.freeze(merged);
232
+ }
233
+ /**
234
+ * Generate a TOTP token
235
+ *
236
+ * @param secret - The secret key
237
+ * @returns The OTP token
238
+ */
239
+ generate(secret) {
240
+ const opts = this.allOptions();
241
+ const secretBytes = secretToBytes(secret, opts.encoding);
242
+ const epochSeconds = Math.floor(opts.epoch / 1e3);
243
+ return (0, import_totp.generateSync)({
244
+ secret: secretBytes,
245
+ algorithm: opts.algorithm,
246
+ digits: opts.digits,
247
+ period: opts.step,
248
+ epoch: epochSeconds,
249
+ t0: 0,
250
+ crypto: opts.crypto
251
+ });
252
+ }
253
+ /**
254
+ * Check if a token is valid for the given secret
255
+ *
256
+ * @param token - The token to verify
257
+ * @param secret - The secret key
258
+ * @returns true if valid
259
+ */
260
+ check(token, secret) {
261
+ const delta = this.checkDelta(token, secret);
262
+ return typeof delta === "number";
263
+ }
264
+ /**
265
+ * Check token and return the time window delta
266
+ *
267
+ * @param token - The token to verify
268
+ * @param secret - The secret key
269
+ * @returns Window delta (0 = current, positive = future, negative = past), null if invalid
270
+ */
271
+ checkDelta(token, secret) {
272
+ const opts = this.allOptions();
273
+ const secretBytes = secretToBytes(secret, opts.encoding);
274
+ const epochSeconds = Math.floor(opts.epoch / 1e3);
275
+ const step = opts.step;
276
+ const window = opts.window;
277
+ const epochTolerance = parseWindow(window, step);
278
+ try {
279
+ const result = (0, import_totp.verifySync)({
280
+ secret: secretBytes,
281
+ token,
282
+ algorithm: opts.algorithm,
283
+ digits: opts.digits,
284
+ period: step,
285
+ epoch: epochSeconds,
286
+ t0: 0,
287
+ epochTolerance,
288
+ crypto: opts.crypto
289
+ });
290
+ if (!result.valid) {
291
+ return null;
292
+ }
293
+ return result.delta;
294
+ } catch {
295
+ return null;
296
+ }
297
+ }
298
+ /**
299
+ * Verify a token (object-based API)
300
+ *
301
+ * @param opts - Verification options
302
+ * @returns true if valid
303
+ */
304
+ verify(opts) {
305
+ if (typeof opts !== "object") {
306
+ throw new Error("Expecting argument 0 of verify to be an object");
307
+ }
308
+ return this.check(opts.token, opts.secret);
309
+ }
310
+ /**
311
+ * Generate an otpauth:// URI for TOTP
312
+ *
313
+ * @param accountName - Account name for the URI
314
+ * @param issuer - Issuer name
315
+ * @param secret - The secret key (should be Base32 for QR codes)
316
+ * @returns The otpauth:// URI
317
+ */
318
+ keyuri(accountName, issuer, secret) {
319
+ const opts = this.allOptions();
320
+ return (0, import_uri2.generateTOTP)({
321
+ label: accountName,
322
+ issuer,
323
+ secret,
324
+ algorithm: opts.algorithm,
325
+ digits: opts.digits,
326
+ period: opts.step
327
+ });
328
+ }
329
+ /**
330
+ * Get time used in current step (seconds elapsed in current window)
331
+ *
332
+ * @returns Seconds used in current step
333
+ */
334
+ timeUsed() {
335
+ const opts = this.allOptions();
336
+ const epochSeconds = Math.floor(opts.epoch / 1e3);
337
+ return epochSeconds % opts.step;
338
+ }
339
+ /**
340
+ * Get time remaining until next token
341
+ *
342
+ * @returns Seconds remaining in current step
343
+ */
344
+ timeRemaining() {
345
+ const opts = this.allOptions();
346
+ const epochSeconds = Math.floor(opts.epoch / 1e3);
347
+ return (0, import_totp.getRemainingTime)(epochSeconds, opts.step, 0);
348
+ }
349
+ };
350
+
351
+ // src/authenticator.ts
352
+ var import_core2 = require("@otplib/core");
353
+ var import_plugin_base32_scure3 = require("@otplib/plugin-base32-scure");
354
+ var import_plugin_crypto_noble3 = require("@otplib/plugin-crypto-noble");
355
+ var import_totp2 = require("@otplib/totp");
356
+ var defaultCrypto3 = new import_plugin_crypto_noble3.NobleCryptoPlugin();
357
+ var defaultBase323 = new import_plugin_base32_scure3.ScureBase32Plugin();
358
+ function defaultKeyEncoder(secret, _encoding) {
359
+ const bytes = new TextEncoder().encode(secret);
360
+ return defaultBase323.encode(bytes);
361
+ }
362
+ function defaultKeyDecoder(encodedSecret, _encoding) {
363
+ const bytes = defaultBase323.decode(encodedSecret);
364
+ return new TextDecoder().decode(bytes);
365
+ }
366
+ var Authenticator = class _Authenticator extends TOTP {
367
+ constructor(defaultOptions = {}) {
368
+ super(defaultOptions);
369
+ }
370
+ /**
371
+ * Creates a new Authenticator instance with the specified default options
372
+ */
373
+ create(defaultOptions = {}) {
374
+ return new _Authenticator(defaultOptions);
375
+ }
376
+ /**
377
+ * Returns class options polyfilled with Authenticator default values
378
+ */
379
+ allOptions() {
380
+ const merged = {
381
+ algorithm: HashAlgorithms.SHA1,
382
+ digits: 6,
383
+ encoding: KeyEncodings.HEX,
384
+ epoch: Date.now(),
385
+ step: 30,
386
+ window: 0,
387
+ keyEncoder: defaultKeyEncoder,
388
+ keyDecoder: defaultKeyDecoder,
389
+ crypto: defaultCrypto3,
390
+ base32: defaultBase323,
391
+ ...this._defaultOptions,
392
+ ...this._options
393
+ };
394
+ return Object.freeze(merged);
395
+ }
396
+ /**
397
+ * Generate an OTP token from a Base32 secret
398
+ *
399
+ * @param secret - Base32-encoded secret
400
+ * @returns The OTP token
401
+ */
402
+ generate(secret) {
403
+ const opts = this.allOptions();
404
+ const secretBytes = defaultBase323.decode(secret);
405
+ const epoch = opts.epoch;
406
+ const epochSeconds = epoch >= 1e12 ? Math.floor(epoch / 1e3) : epoch;
407
+ return (0, import_totp2.generateSync)({
408
+ secret: secretBytes,
409
+ algorithm: opts.algorithm,
410
+ digits: opts.digits,
411
+ period: opts.step,
412
+ epoch: epochSeconds,
413
+ t0: 0,
414
+ crypto: opts.crypto
415
+ });
416
+ }
417
+ /**
418
+ * Check if a token is valid for the given Base32 secret
419
+ *
420
+ * @param token - The token to verify
421
+ * @param secret - Base32-encoded secret
422
+ * @returns true if valid
423
+ */
424
+ check(token, secret) {
425
+ const delta = this.checkDelta(token, secret);
426
+ return typeof delta === "number";
427
+ }
428
+ /**
429
+ * Check token and return the time window delta
430
+ *
431
+ * @param token - The token to verify
432
+ * @param secret - Base32-encoded secret
433
+ * @returns Window delta (0 = current, positive = future, negative = past), null if invalid
434
+ */
435
+ checkDelta(token, secret) {
436
+ const opts = this.allOptions();
437
+ const secretBytes = defaultBase323.decode(secret);
438
+ const epoch = opts.epoch;
439
+ const epochSeconds = epoch >= 1e12 ? Math.floor(epoch / 1e3) : epoch;
440
+ const step = opts.step;
441
+ const window = opts.window;
442
+ let epochTolerance = 0;
443
+ if (typeof window === "number") {
444
+ epochTolerance = window * step;
445
+ } else if (Array.isArray(window)) {
446
+ epochTolerance = [window[0] * step, window[1] * step];
447
+ }
448
+ try {
449
+ const result = (0, import_totp2.verifySync)({
450
+ secret: secretBytes,
451
+ token,
452
+ algorithm: opts.algorithm,
453
+ digits: opts.digits,
454
+ period: step,
455
+ epoch: epochSeconds,
456
+ t0: 0,
457
+ epochTolerance,
458
+ crypto: opts.crypto
459
+ });
460
+ if (!result.valid) {
461
+ return null;
462
+ }
463
+ return result.delta;
464
+ } catch {
465
+ return null;
466
+ }
467
+ }
468
+ /**
469
+ * Verify a token (object-based API)
470
+ *
471
+ * @param opts - Verification options
472
+ * @returns true if valid
473
+ */
474
+ verify(opts) {
475
+ if (typeof opts !== "object") {
476
+ throw new Error("Expecting argument 0 of verify to be an object");
477
+ }
478
+ return this.check(opts.token, opts.secret);
479
+ }
480
+ /**
481
+ * Encode a raw secret to Base32
482
+ *
483
+ * @param secret - Raw secret string
484
+ * @returns Base32-encoded secret
485
+ */
486
+ encode(secret) {
487
+ const opts = this.allOptions();
488
+ if (opts.keyEncoder) {
489
+ return opts.keyEncoder(secret, opts.encoding);
490
+ }
491
+ return defaultKeyEncoder(secret, opts.encoding);
492
+ }
493
+ /**
494
+ * Decode a Base32 secret to raw string
495
+ *
496
+ * @param secret - Base32-encoded secret
497
+ * @returns Raw secret string
498
+ */
499
+ decode(secret) {
500
+ const opts = this.allOptions();
501
+ if (opts.keyDecoder) {
502
+ return opts.keyDecoder(secret, opts.encoding);
503
+ }
504
+ return defaultKeyDecoder(secret, opts.encoding);
505
+ }
506
+ /**
507
+ * Generate a random Base32-encoded secret
508
+ *
509
+ * @param numberOfBytes - Number of bytes for the secret (default: 20)
510
+ * @returns Base32-encoded secret
511
+ */
512
+ generateSecret(numberOfBytes = 20) {
513
+ const opts = this.allOptions();
514
+ return (0, import_core2.generateSecret)({
515
+ crypto: opts.crypto,
516
+ base32: opts.base32,
517
+ length: numberOfBytes
518
+ });
519
+ }
520
+ };
521
+
522
+ // src/index.ts
523
+ var import_plugin_crypto_noble4 = require("@otplib/plugin-crypto-noble");
524
+ var import_plugin_base32_scure4 = require("@otplib/plugin-base32-scure");
525
+ var hotp = new HOTP();
526
+ var totp = new TOTP();
527
+ var authenticator = new Authenticator();
528
+ // Annotate the CommonJS export names for ESM import in node:
529
+ 0 && (module.exports = {
530
+ Authenticator,
531
+ HOTP,
532
+ HashAlgorithms,
533
+ KeyEncodings,
534
+ NobleCryptoPlugin,
535
+ ScureBase32Plugin,
536
+ TOTP,
537
+ authenticator,
538
+ hotp,
539
+ hotpDigestToToken,
540
+ totp
541
+ });
542
+ //# sourceMappingURL=index.cjs.map