@antmind/otp 0.1.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.
@@ -0,0 +1,31 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+
13
+ strategy:
14
+ matrix:
15
+ node-version: [22.x, 24.x, 25.x]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v6
19
+ - name: Use Node.js ${{ matrix.node-version }}
20
+ uses: actions/setup-node@v6
21
+ with:
22
+ node-version: ${{ matrix.node-version }}
23
+ - run: npm ci
24
+ - run: npm run build --if-present
25
+ - run: npm test
26
+ env:
27
+ CI: true
28
+ - name: Upload coverage reports to Codecov
29
+ uses: codecov/codecov-action@v5
30
+ with:
31
+ token: ${{ secrets.CODECOV_TOKEN }}
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Chen Su
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,88 @@
1
+ # @antmind/otp
2
+
3
+ A small, dependency-free TypeScript library for generating and verifying one-time passwords (OTP): HOTP and TOTP.
4
+
5
+ ## Features
6
+
7
+ - RFC 4226 compliant HOTP
8
+ - RFC 6238 compliant TOTP
9
+ - Supports SHA-1, SHA-256, and SHA-512 hashing algorithms
10
+ - Configurable code length
11
+ - URI generation for easy integration with authenticator apps
12
+ - Compatible with Google Authenticator and similar apps
13
+ - Node.js and browser compatible
14
+ - Simple API with TypeScript types
15
+
16
+ ## Install
17
+
18
+ Run the following command to install the package:
19
+
20
+ ```bash
21
+ npm install @antmind/otp
22
+ ```
23
+
24
+ ## Getting Started
25
+
26
+ TOTP is time-based and defined by the HOTP of a time counter. Common parameters are a time step (`X`) and start time `T0`.
27
+
28
+ The time counter is
29
+
30
+ $$
31
+ T = \left\lfloor \frac{\text{UnixTime} - T_0}{X} \right\rfloor
32
+ $$
33
+
34
+ Generate and verify a TOTP:
35
+
36
+ ```ts
37
+ import { TOTP } from '@antmind/otp'
38
+
39
+ const secret = '12345678901234567890';
40
+ const digits = 6;
41
+ const step = 30; // seconds
42
+
43
+ const totp = new TOTP({ digits, period: step });
44
+ const code = await totp.generate(secret);
45
+ console.log('TOTP:', code);
46
+
47
+ const valid = await totp.verify(secret, code);
48
+ console.log('valid:', valid)
49
+ ```
50
+
51
+ Adjust `step` and `digits` to match the authenticator you are using.
52
+
53
+ ## API reference
54
+
55
+ - `class TOTP`: TOTP generator and verifier class.
56
+ - `generate(secret: string, time?: number): Promise<string>`: Generates a TOTP code for the given secret and time, defaulting to the current time.
57
+ - `verify(secret: string, token: string, time?: number): Promise<boolean>`: Verifies a TOTP code for the given secret and time.
58
+ - `getURI(secret: string, account: string): string`: Generates an otpauth URI for the TOTP configuration.
59
+ - `class HOTP`: HOTP generator and verifier class.
60
+ - `generate(secret: string, counter: number): Promise<string>`: Generates an HOTP code for the given secret and counter.
61
+ - `verify(secret: string, counter: number, token: string): Promise<boolean>`: Verifies an HOTP code for the given secret and counter.
62
+ - `getURI(secret: string, account: string, counter?: number): string`: Generates an otpauth URI for the HOTP configuration.
63
+
64
+ ## Testing
65
+
66
+ Run the test suite with:
67
+
68
+ ```bash
69
+ npm test
70
+ ```
71
+
72
+ Unit tests are located in the `tests/` folder and use Jest.
73
+
74
+ ## Contributing
75
+
76
+ Contributions are welcome. Please follow these steps:
77
+
78
+ 1. Fork the repository.
79
+ 2. Create a feature branch: `git checkout -b feat/my-feature`.
80
+ 3. Implement your changes and tests.
81
+ 4. Run `npm run build` and `npm test`.
82
+ 5. Open a pull request describing your changes.
83
+
84
+ Please keep changes small and focused. If you plan larger changes, open an issue first to discuss the design.
85
+
86
+ ## License
87
+
88
+ This project is open-sourced under the terms of the MIT License. See the `LICENSE` file for details.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The supported HMAC algorithms.
3
+ */
4
+ export type Algorithm = 'SHA1' | 'SHA256' | 'SHA512';
5
+ /**
6
+ * Generates an HMAC digest for the given message using the specified algorithm and key.
7
+ *
8
+ * @param algorithm The HMAC algorithm to use. Default is 'SHA1'.
9
+ * @param key The secret key used for HMAC generation.
10
+ * @param message The message to be hashed.
11
+ * @returns The generated HMAC digest as a Uint8Array.
12
+ */
13
+ export declare const hmac: (algorithm: Algorithm | undefined, key: Uint8Array, message: Uint8Array) => Promise<Uint8Array<ArrayBuffer>>;
14
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAErD;;;;;;;GAOG;AACH,eAAO,MAAM,IAAI,GACf,WAAW,SAAS,YAAS,EAC7B,KAAK,UAAU,EACf,SAAS,UAAU,qCAMpB,CAAA"}
package/dist/crypto.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hmac = void 0;
4
+ const crypto_1 = require("crypto");
5
+ /**
6
+ * Generates an HMAC digest for the given message using the specified algorithm and key.
7
+ *
8
+ * @param algorithm The HMAC algorithm to use. Default is 'SHA1'.
9
+ * @param key The secret key used for HMAC generation.
10
+ * @param message The message to be hashed.
11
+ * @returns The generated HMAC digest as a Uint8Array.
12
+ */
13
+ const hmac = async (algorithm = 'SHA1', key, message) => {
14
+ const hmac = (0, crypto_1.createHmac)(algorithm, key);
15
+ hmac.update(message);
16
+ return new Uint8Array(hmac.digest());
17
+ };
18
+ exports.hmac = hmac;
19
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":";;;AAAA,mCAAoC;AAOpC;;;;;;;GAOG;AACI,MAAM,IAAI,GAAG,KAAK,EACvB,YAAuB,MAAM,EAC7B,GAAe,EACf,OAAmB,EACnB,EAAE;IACF,MAAM,IAAI,GAAG,IAAA,mBAAU,EAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IACxC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAErB,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;AACvC,CAAC,CAAA;AATY,QAAA,IAAI,QAShB"}
package/dist/hotp.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import { OTP } from './otp';
2
+ /**
3
+ * HMAC-based One-Time Password (HOTP) generator and verifier.
4
+ */
5
+ export declare class HOTP extends OTP {
6
+ /**
7
+ * Generates an HOTP code based on the provided secret and counter.
8
+ *
9
+ * @param secret The shared secret key used for generating the HOTP code.
10
+ * @param counter The counter value used for generating the HOTP code.
11
+ * @returns The generated HOTP code as a string.
12
+ */
13
+ generate(secret: string, counter?: number): Promise<string>;
14
+ /**
15
+ * Verifies the provided HOTP code against the generated code for the given secret and counter.
16
+ *
17
+ * @param secret The shared secret key used for generating the HOTP code.
18
+ * @param token The HOTP code to verify.
19
+ * @param counter The counter value used for generating the HOTP code.
20
+ * @returns A boolean indicating whether the HOTP code is valid.
21
+ */
22
+ verify(secret: string, token: string, counter?: number): Promise<boolean>;
23
+ /**
24
+ * Generates the OTP URI for the given parameters.
25
+ *
26
+ * @param secret The shared secret key used for generating the HOTP code.
27
+ * @param accountName The account name associated with the OTP.
28
+ * @param counter The counter value used for generating the HOTP code.
29
+ * @returns The generated OTP URI as a string.
30
+ */
31
+ getURI(secret: string, accountName: string, counter?: number): string;
32
+ }
33
+ //# sourceMappingURL=hotp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hotp.d.ts","sourceRoot":"","sources":["../src/hotp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAE5B;;GAEG;AACH,qBAAa,IAAK,SAAQ,GAAG;IAC3B;;;;;;OAMG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIjE;;;;;;;OAOG;IACG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI/E;;;;;;;OAOG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM;CAStE"}
package/dist/hotp.js ADDED
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HOTP = void 0;
4
+ const otp_1 = require("./otp");
5
+ /**
6
+ * HMAC-based One-Time Password (HOTP) generator and verifier.
7
+ */
8
+ class HOTP extends otp_1.OTP {
9
+ /**
10
+ * Generates an HOTP code based on the provided secret and counter.
11
+ *
12
+ * @param secret The shared secret key used for generating the HOTP code.
13
+ * @param counter The counter value used for generating the HOTP code.
14
+ * @returns The generated HOTP code as a string.
15
+ */
16
+ async generate(secret, counter) {
17
+ return this.generateCode(secret, counter || 0);
18
+ }
19
+ /**
20
+ * Verifies the provided HOTP code against the generated code for the given secret and counter.
21
+ *
22
+ * @param secret The shared secret key used for generating the HOTP code.
23
+ * @param token The HOTP code to verify.
24
+ * @param counter The counter value used for generating the HOTP code.
25
+ * @returns A boolean indicating whether the HOTP code is valid.
26
+ */
27
+ async verify(secret, token, counter) {
28
+ return this.verifyCode(secret, token, counter || 0);
29
+ }
30
+ /**
31
+ * Generates the OTP URI for the given parameters.
32
+ *
33
+ * @param secret The shared secret key used for generating the HOTP code.
34
+ * @param accountName The account name associated with the OTP.
35
+ * @param counter The counter value used for generating the HOTP code.
36
+ * @returns The generated OTP URI as a string.
37
+ */
38
+ getURI(secret, accountName, counter) {
39
+ const params = {};
40
+ if (counter) {
41
+ params['counter'] = counter.toString();
42
+ }
43
+ return this.buildURI('hotp', secret, accountName, params);
44
+ }
45
+ }
46
+ exports.HOTP = HOTP;
47
+ //# sourceMappingURL=hotp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hotp.js","sourceRoot":"","sources":["../src/hotp.ts"],"names":[],"mappings":";;;AAAA,+BAA4B;AAE5B;;GAEG;AACH,MAAa,IAAK,SAAQ,SAAG;IAC3B;;;;;;OAMG;IACH,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,OAAgB;QAC7C,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC;IACjD,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,MAAM,CAAC,MAAc,EAAE,KAAa,EAAE,OAAgB;QAC1D,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC;IACtD,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,MAAc,EAAE,WAAmB,EAAE,OAAgB;QAC1D,MAAM,MAAM,GAA2B,EAAE,CAAC;QAE1C,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzC,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IAC5D,CAAC;CACF;AAzCD,oBAyCC"}
@@ -0,0 +1,4 @@
1
+ export { Algorithm } from './crypto';
2
+ export { TOTP, TOTPOptions } from './totp';
3
+ export { HOTP } from './hotp';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HOTP = exports.TOTP = void 0;
4
+ var totp_1 = require("./totp");
5
+ Object.defineProperty(exports, "TOTP", { enumerable: true, get: function () { return totp_1.TOTP; } });
6
+ var hotp_1 = require("./hotp");
7
+ Object.defineProperty(exports, "HOTP", { enumerable: true, get: function () { return hotp_1.HOTP; } });
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,+BAA2C;AAAlC,4FAAA,IAAI,OAAA;AACb,+BAA8B;AAArB,4FAAA,IAAI,OAAA"}
package/dist/otp.d.ts ADDED
@@ -0,0 +1,72 @@
1
+ import { Algorithm } from './crypto';
2
+ export interface OTPOptions {
3
+ /**
4
+ * The algorithm used for the OTP generation, typically 'SHA1', 'SHA256', or 'SHA512'.
5
+ * Default is 'SHA1'.
6
+ */
7
+ algorithm?: Algorithm;
8
+ /**
9
+ * The number of digits in the generated OTP, typically 6 or 8. Default is 6.
10
+ */
11
+ digits?: number;
12
+ /**
13
+ * The issuer name to be included in the OTP URI.
14
+ */
15
+ issuer?: string;
16
+ }
17
+ export declare abstract class OTP {
18
+ /**
19
+ * The algorithm used for the OTP generation, typically 'SHA1', 'SHA256', or 'SHA512'.
20
+ * Default is 'SHA1'.
21
+ */
22
+ algorithm?: Algorithm;
23
+ /**
24
+ * The number of digits in the generated OTP, typically 6 or 8. Default is 6.
25
+ */
26
+ digits?: number;
27
+ /**
28
+ * The issuer name to be included in the OTP URI.
29
+ */
30
+ issuer?: string | undefined;
31
+ private base32?;
32
+ constructor(options?: OTPOptions);
33
+ abstract generate(secret: string, counterOrTime?: number): Promise<string>;
34
+ abstract verify(secret: string, token: string, counterOrTime?: number): Promise<boolean>;
35
+ /**
36
+ * Verifies the provided OTP code against the generated code for the given secret and counter or
37
+ * time.
38
+ *
39
+ * @param secret The shared secret key used for generating the OTP code.
40
+ * @param token The OTP code to verify.
41
+ * @param counter The counter or time value used for generating the OTP code.
42
+ * @returns A boolean indicating whether the OTP code is valid.
43
+ */
44
+ protected verifyCode(secret: string, token: string, counter: number): Promise<boolean>;
45
+ /**
46
+ * Generates an OTP code based on the provided secret and counter or time.
47
+ *
48
+ * @param secret The shared secret key used for generating the OTP code.
49
+ * @param counter The counter or time value used for generating the OTP code.
50
+ * @returns The generated OTP code as a string.
51
+ */
52
+ protected generateCode(secret: string, counter: number): Promise<string>;
53
+ /**
54
+ * Builds the OTP URI for the given OTP type, secret, account name, and other parameters.
55
+ *
56
+ * @param type The type of OTP, either 'totp' or 'hotp'.
57
+ * @param secret The shared secret key used for generating the OTP code.
58
+ * @param accountName The account name associated with the OTP.
59
+ * @param otherParams Additional parameters to include in the OTP URI.
60
+ * @returns The generated OTP URI as a string.
61
+ */
62
+ protected buildURI(type: 'totp' | 'hotp', secret: string, accountName: string, otherParams: {
63
+ [key: string]: string | number;
64
+ }): string;
65
+ /**
66
+ * Gets the Base32 encoding instance.
67
+ *
68
+ * @returns The base32 encoding instance.
69
+ */
70
+ private getBase32;
71
+ }
72
+ //# sourceMappingURL=otp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otp.d.ts","sourceRoot":"","sources":["../src/otp.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAQ,MAAM,UAAU,CAAC;AAK3C,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,8BAAsB,GAAG;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE5B,OAAO,CAAC,MAAM,CAAC,CAAiB;gBAEpB,OAAO,CAAC,EAAE,UAAU;IAMhC,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAE1E,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAExF;;;;;;;;OAQG;cACa,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM5F;;;;;;OAMG;cACa,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkB9E;;;;;;;;OAQG;IACH,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE;QAC1F,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;KAChC,GAAG,MAAM;IA4BV;;;;OAIG;IACH,OAAO,CAAC,SAAS;CAOlB"}
package/dist/otp.js ADDED
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OTP = void 0;
4
+ const encoding_1 = require("@antmind/encoding");
5
+ const crypto_1 = require("./crypto");
6
+ const DefaultAlgorithm = 'SHA1';
7
+ const DefaultDigits = 6;
8
+ class OTP {
9
+ /**
10
+ * The algorithm used for the OTP generation, typically 'SHA1', 'SHA256', or 'SHA512'.
11
+ * Default is 'SHA1'.
12
+ */
13
+ algorithm;
14
+ /**
15
+ * The number of digits in the generated OTP, typically 6 or 8. Default is 6.
16
+ */
17
+ digits;
18
+ /**
19
+ * The issuer name to be included in the OTP URI.
20
+ */
21
+ issuer;
22
+ base32;
23
+ constructor(options) {
24
+ this.algorithm = options?.algorithm || DefaultAlgorithm;
25
+ this.digits = options?.digits || DefaultDigits;
26
+ this.issuer = options?.issuer;
27
+ }
28
+ /**
29
+ * Verifies the provided OTP code against the generated code for the given secret and counter or
30
+ * time.
31
+ *
32
+ * @param secret The shared secret key used for generating the OTP code.
33
+ * @param token The OTP code to verify.
34
+ * @param counter The counter or time value used for generating the OTP code.
35
+ * @returns A boolean indicating whether the OTP code is valid.
36
+ */
37
+ async verifyCode(secret, token, counter) {
38
+ const generatedCode = await this.generateCode(secret, counter);
39
+ return generatedCode === token;
40
+ }
41
+ /**
42
+ * Generates an OTP code based on the provided secret and counter or time.
43
+ *
44
+ * @param secret The shared secret key used for generating the OTP code.
45
+ * @param counter The counter or time value used for generating the OTP code.
46
+ * @returns The generated OTP code as a string.
47
+ */
48
+ async generateCode(secret, counter) {
49
+ const message = new Uint8Array(8);
50
+ const view = new DataView(message.buffer);
51
+ view.setUint32(4, counter, false);
52
+ const secretBytes = new TextEncoder().encode(secret);
53
+ const hash = await (0, crypto_1.hmac)(this.algorithm, secretBytes, message);
54
+ const offset = (hash[hash.length - 1] || 0) & 0x0f;
55
+ const code = (((hash[offset] || 0) & 0x7F) << 24)
56
+ | (((hash[offset + 1] || 0) & 0xFF) << 16)
57
+ | (((hash[offset + 2] || 0) & 0xFF) << 8)
58
+ | ((hash[offset + 3] || 0) & 0xFF);
59
+ const mod = 10 ** (this.digits || DefaultDigits);
60
+ const otp = (code % mod).toString().padStart(this.digits || DefaultDigits, '0');
61
+ return otp;
62
+ }
63
+ /**
64
+ * Builds the OTP URI for the given OTP type, secret, account name, and other parameters.
65
+ *
66
+ * @param type The type of OTP, either 'totp' or 'hotp'.
67
+ * @param secret The shared secret key used for generating the OTP code.
68
+ * @param accountName The account name associated with the OTP.
69
+ * @param otherParams Additional parameters to include in the OTP URI.
70
+ * @returns The generated OTP URI as a string.
71
+ */
72
+ buildURI(type, secret, accountName, otherParams) {
73
+ const params = new URLSearchParams();
74
+ const base32 = this.getBase32();
75
+ const secretBase32 = base32.encode(secret);
76
+ params.append('secret', secretBase32);
77
+ if (this.issuer) {
78
+ params.append('issuer', this.issuer);
79
+ }
80
+ if (this.algorithm && this.algorithm !== DefaultAlgorithm) {
81
+ params.append('algorithm', this.algorithm);
82
+ }
83
+ if (this.digits && this.digits !== DefaultDigits) {
84
+ params.append('digits', (this.digits).toString());
85
+ }
86
+ for (const [key, value] of Object.entries(otherParams)) {
87
+ params.append(key, value.toString());
88
+ }
89
+ const path = this.issuer
90
+ ? `${encodeURIComponent(this.issuer)}:${encodeURIComponent(accountName)}`
91
+ : encodeURIComponent(accountName);
92
+ return `otpauth://${type}/${path}?${params.toString()}`;
93
+ }
94
+ /**
95
+ * Gets the Base32 encoding instance.
96
+ *
97
+ * @returns The base32 encoding instance.
98
+ */
99
+ getBase32() {
100
+ if (!this.base32) {
101
+ this.base32 = new encoding_1.Base32Encoding({ padChar: '' });
102
+ }
103
+ return this.base32;
104
+ }
105
+ }
106
+ exports.OTP = OTP;
107
+ //# sourceMappingURL=otp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otp.js","sourceRoot":"","sources":["../src/otp.ts"],"names":[],"mappings":"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"}
package/dist/totp.d.ts ADDED
@@ -0,0 +1,45 @@
1
+ import { OTP, OTPOptions } from './otp';
2
+ export interface TOTPOptions extends OTPOptions {
3
+ /**
4
+ * The time period in seconds for which a TOTP code is valid. Default is 30 seconds.
5
+ */
6
+ period?: number;
7
+ }
8
+ /**
9
+ * Time-based One-Time Password (TOTP) generator and verifier.
10
+ */
11
+ export declare class TOTP extends OTP {
12
+ /**
13
+ * The time period in seconds for which a TOTP code is valid. Default is 30 seconds.
14
+ */
15
+ period?: number;
16
+ constructor(options?: TOTPOptions);
17
+ /**
18
+ * Generates a TOTP code for the given secret and time.
19
+ *
20
+ * @param secret The shared secret key used for generating the TOTP code.
21
+ * @param time The specific time (in seconds since epoch) for which to generate the TOTP code.
22
+ * If not provided, the current time will be used.
23
+ * @returns The generated TOTP code as a string.
24
+ */
25
+ generate(secret: string, time?: number): Promise<string>;
26
+ /**
27
+ * Verifies a TOTP code for the given secret and time.
28
+ *
29
+ * @param secret The shared secret key used for generating the TOTP code.
30
+ * @param token The TOTP code to verify.
31
+ * @param time The specific time (in seconds since epoch) for which to verify the TOTP code.
32
+ * If not provided, the current time will be used.
33
+ * @returns A boolean indicating whether the TOTP code is valid.
34
+ */
35
+ verify(secret: string, token: string, time?: number): Promise<boolean>;
36
+ /**
37
+ * Generates the OTP URI for provisioning.
38
+ *
39
+ * @param secret The shared secret key used for generating the TOTP code.
40
+ * @param accountName The account name (e.g., user email) to be included in the URI.
41
+ * @returns The generated OTP URI as a string.
42
+ */
43
+ getURI(secret: string, accountName: string): string;
44
+ }
45
+ //# sourceMappingURL=totp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"totp.d.ts","sourceRoot":"","sources":["../src/totp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAIxC,MAAM,WAAW,WAAY,SAAQ,UAAU;IAC7C;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,qBAAa,IAAK,SAAQ,GAAG;IAC3B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEJ,OAAO,CAAC,EAAE,WAAW;IAKjC;;;;;;;OAOG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAS9D;;;;;;;;OAQG;IACG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS5E;;;;;;OAMG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM;CASpD"}
package/dist/totp.js ADDED
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TOTP = void 0;
4
+ const otp_1 = require("./otp");
5
+ const DefaultPeriod = 30;
6
+ /**
7
+ * Time-based One-Time Password (TOTP) generator and verifier.
8
+ */
9
+ class TOTP extends otp_1.OTP {
10
+ /**
11
+ * The time period in seconds for which a TOTP code is valid. Default is 30 seconds.
12
+ */
13
+ period;
14
+ constructor(options) {
15
+ super(options);
16
+ this.period = options?.period || DefaultPeriod;
17
+ }
18
+ /**
19
+ * Generates a TOTP code for the given secret and time.
20
+ *
21
+ * @param secret The shared secret key used for generating the TOTP code.
22
+ * @param time The specific time (in seconds since epoch) for which to generate the TOTP code.
23
+ * If not provided, the current time will be used.
24
+ * @returns The generated TOTP code as a string.
25
+ */
26
+ async generate(secret, time) {
27
+ if (!time) {
28
+ time = Math.floor(Date.now() / 1000);
29
+ }
30
+ const counter = Math.floor(time / (this.period || DefaultPeriod));
31
+ return this.generateCode(secret, counter);
32
+ }
33
+ /**
34
+ * Verifies a TOTP code for the given secret and time.
35
+ *
36
+ * @param secret The shared secret key used for generating the TOTP code.
37
+ * @param token The TOTP code to verify.
38
+ * @param time The specific time (in seconds since epoch) for which to verify the TOTP code.
39
+ * If not provided, the current time will be used.
40
+ * @returns A boolean indicating whether the TOTP code is valid.
41
+ */
42
+ async verify(secret, token, time) {
43
+ if (!time) {
44
+ time = Math.floor(Date.now() / 1000);
45
+ }
46
+ const counter = Math.floor(time / (this.period || DefaultPeriod));
47
+ return this.verifyCode(secret, token, counter);
48
+ }
49
+ /**
50
+ * Generates the OTP URI for provisioning.
51
+ *
52
+ * @param secret The shared secret key used for generating the TOTP code.
53
+ * @param accountName The account name (e.g., user email) to be included in the URI.
54
+ * @returns The generated OTP URI as a string.
55
+ */
56
+ getURI(secret, accountName) {
57
+ const params = {};
58
+ if (this.period && this.period !== DefaultPeriod) {
59
+ params['period'] = this.period;
60
+ }
61
+ return this.buildURI('totp', secret, accountName, params);
62
+ }
63
+ }
64
+ exports.TOTP = TOTP;
65
+ //# sourceMappingURL=totp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"totp.js","sourceRoot":"","sources":["../src/totp.ts"],"names":[],"mappings":";;;AAAA,+BAAwC;AAExC,MAAM,aAAa,GAAG,EAAE,CAAC;AASzB;;GAEG;AACH,MAAa,IAAK,SAAQ,SAAG;IAC3B;;OAEG;IACH,MAAM,CAAU;IAEhB,YAAY,OAAqB;QAC/B,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,aAAa,CAAC;IACjD,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,IAAa;QAC1C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QACvC,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,aAAa,CAAC,CAAC,CAAC;QAElE,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,MAAM,CAAC,MAAc,EAAE,KAAa,EAAE,IAAa;QACvD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QACvC,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,aAAa,CAAC,CAAC,CAAC;QAElE,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,MAAc,EAAE,WAAmB;QACxC,MAAM,MAAM,GAAoC,EAAE,CAAC;QAEnD,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;YACjD,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QACjC,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IAC5D,CAAC;CACF;AA9DD,oBA8DC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Encodes a string into a Uint8Array using UTF-8 encoding.
3
+ *
4
+ * @param str The string to encode.
5
+ * @returns The encoded Uint8Array.
6
+ */
7
+ export declare const encodeString: (str: string) => Uint8Array;
8
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,UAE1C,CAAC"}
package/dist/utils.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.encodeString = void 0;
4
+ /**
5
+ * Encodes a string into a Uint8Array using UTF-8 encoding.
6
+ *
7
+ * @param str The string to encode.
8
+ * @returns The encoded Uint8Array.
9
+ */
10
+ const encodeString = (str) => {
11
+ return new TextEncoder().encode(str);
12
+ };
13
+ exports.encodeString = encodeString;
14
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACI,MAAM,YAAY,GAAG,CAAC,GAAW,EAAc,EAAE;IACtD,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AACvC,CAAC,CAAC;AAFW,QAAA,YAAY,gBAEvB"}
package/jest.config.js ADDED
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'node',
4
+ testMatch: ['**/tests/**/*.test.ts'],
5
+ collectCoverage: true,
6
+ coverageDirectory: 'coverage',
7
+ coverageReporters: ['text', 'lcov']
8
+ };