@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 +21 -0
- package/README.md +53 -0
- package/dist/index.cjs +542 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +427 -0
- package/dist/index.d.ts +427 -0
- package/dist/index.js +509 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -0
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
|