@nowarajs/totp 1.1.12 β†’ 1.2.1

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/README.md CHANGED
@@ -1,41 +1,32 @@
1
- # πŸ” NowaraJS - TOTP
1
+ # πŸ” NowaraJS TOTP
2
+
3
+ Let's be honest: there are already packages like `totp-generator` that do this. I built this one mostly for myselfβ€”to learn how TOTP/HOTP actually works under the hood, and to have a lightweight alternative I fully understand.
4
+
5
+ ## Why this package?
6
+
7
+ No grand mission here. I wanted:
8
+ 1. **To learn** how RFC 6238/4226 work in practice
9
+ 2. **A tiny footprint** without pulling half of npm
10
+ 3. **Something I control** for my own projects
11
+
12
+ If you're looking for battle-tested libraries, check out the established ones. If you want something small and readable, this might be for you.
2
13
 
3
14
  ## πŸ“Œ Table of Contents
4
15
 
5
- - [πŸ” NowaraJS - TOTP](#-nowarajs---totp)
6
- - [πŸ“Œ Table of Contents](#-table-of-contents)
7
- - [πŸ“ Description](#-description)
8
- - [✨ Features](#-features)
9
- - [πŸ”§ Installation](#-installation)
10
- - [βš™οΈ Usage](#-usage)
11
- - [Basic TOTP Generation](#basic-totp-generation)
12
- - [TOTP Verification](#totp-verification)
13
- - [HOTP Support](#hotp-support)
14
- - [OTPAuth URI Generation](#otpauth-uri-generation)
15
- - [Secret Generation](#secret-generation)
16
- - [πŸ”‘ Advanced Configuration](#-advanced-configuration)
17
- - [πŸ› οΈ Utilities](#-utilities)
18
- - [πŸ“š API Reference](#-api-reference)
19
- - [βš–οΈ License](#-license)
20
- - [πŸ“§ Contact](#-contact)
21
-
22
- ## πŸ“ Description
23
-
24
- > A comprehensive Time-based One-Time Password (TOTP) and HMAC-based One-Time Password (HOTP) implementation for TypeScript/JavaScript.
25
-
26
- **NowaraJS TOTP** provides a secure and RFC-compliant implementation of TOTP and HOTP algorithms with full support for QR code generation, secret management, and various authentication configurations. Perfect for implementing two-factor authentication (2FA) in your applications.
16
+ - [Features](#-features)
17
+ - [Installation](#-installation)
18
+ - [Usage](#-usage)
19
+ - [API Reference](#-api-reference)
20
+ - [License](#-license)
21
+ - [Contact](#-contact)
27
22
 
28
23
  ## ✨ Features
29
24
 
30
- - πŸ” **RFC 6238 TOTP**: Full RFC-compliant Time-based One-Time Password implementation
31
- - πŸ”‘ **RFC 4226 HOTP**: Complete HMAC-based One-Time Password support
32
- - πŸ“± **QR Code Support**: Generate OTPAuth URIs for easy mobile app integration
33
- - πŸ”’ **Crypto Secure**: Uses Web Crypto API for secure random number generation
34
- - ⚑ **High Performance**: Optimized for speed with minimal dependencies
35
- - πŸ› οΈ **Configurable**: Support for different algorithms (SHA-1, SHA-256, SHA-512)
36
- - πŸ“ **Flexible Digits**: Support for 6-8 digit codes
37
- - ⏰ **Time Window**: Configurable time periods and verification windows
38
- - 🎯 **Base32 Encoding**: Built-in Base32 encoding/decoding utilities
25
+ - πŸ” **RFC Compliant**: Full RFC 6238 (TOTP) and RFC 4226 (HOTP) implementation.
26
+ - πŸ“± **QR Code Ready**: Generate OTPAuth URIs compatible with Google Authenticator, Authy, etc.
27
+ - πŸ”’ **Crypto Secure**: Uses Web Crypto API for truly random secret generation.
28
+ - πŸ› οΈ **Flexible**: SHA-1, SHA-256, SHA-512 algorithms with 6-8 digit codes.
29
+ - πŸ“¦ **Zero Dependencies**: Pure TypeScript, tiny footprint.
39
30
 
40
31
  ## πŸ”§ Installation
41
32
 
@@ -45,168 +36,88 @@ bun add @nowarajs/totp @nowarajs/error
45
36
 
46
37
  ## βš™οΈ Usage
47
38
 
48
- ### Basic TOTP Generation
39
+ ### Generate a TOTP Code
40
+
41
+ Use this when you need to generate a one-time password for the current time window.
49
42
 
50
43
  ```ts
51
44
  import { totp, generateSecretBytes, base32Encode } from '@nowarajs/totp';
52
45
 
53
- // Generate a secret
54
- const secret = generateSecretBytes(20); // 20 bytes = 160 bits
55
- const secretBase32 = base32Encode(secret);
46
+ // Generate a cryptographically secure secret
47
+ const secret = generateSecretBytes(20);
56
48
 
57
- // Generate TOTP code
49
+ // Generate the current TOTP code
58
50
  const code = await totp(secret, {
59
- algorithm: 'SHA-1',
60
- digits: 6,
61
- period: 30
51
+ algorithm: 'SHA-1',
52
+ digits: 6,
53
+ period: 30
62
54
  });
63
55
 
64
- console.log('TOTP Code:', code); // e.g., "123456"
56
+ console.log('Your code:', code); // e.g., "847263"
65
57
  ```
66
58
 
67
- ### TOTP Verification
59
+ ### Verify a User's Code
60
+
61
+ Use this to validate the code your user just entered. The `window` option handles clock drift gracefully.
68
62
 
69
63
  ```ts
70
64
  import { verifyTotp } from '@nowarajs/totp';
71
65
 
72
- // Verify a TOTP code
73
66
  const isValid = await verifyTotp(secret, userInputCode, {
74
- algorithm: 'SHA-1',
75
- digits: 6,
76
- period: 30,
77
- window: 1 // Allow 1 time step before/after current time
67
+ algorithm: 'SHA-1',
68
+ digits: 6,
69
+ period: 30,
70
+ window: 1 // Accept codes from Β±30 seconds
78
71
  });
79
72
 
80
- if (isValid) {
81
- console.log('βœ… Code is valid!');
82
- } else {
83
- console.log('❌ Invalid code');
84
- }
73
+ if (isValid) console.log('βœ… Access granted');
74
+ else console.log('❌ Invalid code');
85
75
  ```
86
76
 
87
- ### HOTP Support
77
+ ### Generate a QR Code URI
88
78
 
89
- ```ts
90
- import { hotp } from '@nowarajs/totp';
91
-
92
- // Generate HOTP code with counter
93
- const counter = 123;
94
- const hotpCode = await hotp(secret, counter, {
95
- algorithm: 'SHA-1',
96
- digits: 6
97
- });
98
-
99
- console.log('HOTP Code:', hotpCode);
100
- ```
101
-
102
- ### OTPAuth URI Generation
79
+ Use this to let users scan a QR code with their authenticator app.
103
80
 
104
81
  ```ts
105
- import { buildOtpAuthUri, base32Encode } from '@nowarajs/totp';
82
+ import { buildOtpAuthUri, generateSecretBytes, base32Encode } from '@nowarajs/totp';
106
83
 
107
84
  const secret = generateSecretBytes(20);
108
85
  const secretBase32 = base32Encode(secret);
109
86
 
110
- // Create URI for QR code
111
87
  const uri = buildOtpAuthUri({
112
- secretBase32,
113
- label: 'user@example.com',
114
- issuer: 'MyApp',
115
- algorithm: 'SHA-1',
116
- digits: 6,
117
- period: 30
88
+ secretBase32,
89
+ label: 'user@example.com',
90
+ issuer: 'MyApp',
91
+ algorithm: 'SHA-1',
92
+ digits: 6,
93
+ period: 30
118
94
  });
119
95
 
120
- console.log('QR Code URI:', uri);
96
+ // Feed this URI to any QR code library
97
+ console.log(uri);
121
98
  // otpauth://totp/user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=MyApp
122
99
  ```
123
100
 
124
- ### Secret Generation
101
+ ### HOTP (Counter-Based)
125
102
 
126
- ```ts
127
- import { generateSecretBytes, base32Encode, base32Decode } from '@nowarajs/totp/utils';
128
-
129
- // Generate cryptographically secure secret
130
- const secret = generateSecretBytes(32); // 256 bits for extra security
131
-
132
- // Encode for storage/transmission
133
- const encoded = base32Encode(secret);
134
- console.log('Base32 Secret:', encoded);
135
-
136
- // Decode when needed
137
- const decoded = base32Decode(encoded);
138
- console.log('Original bytes match:', secret.every((byte, i) => byte === decoded[i]));
139
- ```
140
-
141
- ## πŸ”‘ Advanced Configuration
142
-
143
- ### Custom Algorithm and Settings
103
+ Use this when you need counter-based OTPs instead of time-based ones.
144
104
 
145
105
  ```ts
146
- // Use SHA-256 with 8 digits and 60-second period
147
- const advancedCode = await totp(secret, {
148
- algorithm: 'SHA-256',
149
- digits: 8,
150
- period: 60
151
- });
106
+ import { hotp } from '@nowarajs/totp';
152
107
 
153
- // Verify with larger time window for clock drift tolerance
154
- const isValid = await verifyTotp(secret, userCode, {
155
- algorithm: 'SHA-256',
156
- digits: 8,
157
- period: 60,
158
- window: 2 // Allow Β±2 time steps (Β±2 minutes)
108
+ const code = await hotp(secret, 123, {
109
+ algorithm: 'SHA-1',
110
+ digits: 6
159
111
  });
160
112
  ```
161
113
 
162
- ### Parse Existing OTPAuth URIs
163
-
164
- ```ts
165
- import { parseOtpAuthUri } from '@nowarajs/totp';
166
-
167
- const uri = 'otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example';
168
- const parsed = parseOtpAuthUri(uri);
169
-
170
- console.log(parsed);
171
- // {
172
- // type: 'totp',
173
- // label: 'Example:alice@google.com',
174
- // secret: 'JBSWY3DPEHPK3PXP',
175
- // issuer: 'Example',
176
- // algorithm: 'SHA-1',
177
- // digits: 6,
178
- // period: 30
179
- // }
180
- ```
181
-
182
- ## πŸ› οΈ Utilities
183
-
184
- The package includes several utility functions available through subpath imports:
185
-
186
- ```ts
187
- // Base32 encoding/decoding
188
- import { base32Encode, base32Decode } from '@nowarajs/totp/utils';
189
-
190
- // Secret generation
191
- import { generateSecretBytes } from '@nowarajs/totp/utils';
192
-
193
- // Time utilities
194
- import { timeRemaining } from '@nowarajs/totp/utils';
195
-
196
- // Get seconds until next TOTP generation
197
- const remaining = timeRemaining(30); // for 30-second period
198
- console.log(`Next code in ${remaining} seconds`);
199
- ```
200
-
201
114
  ## πŸ“š API Reference
202
115
 
203
- You can find the complete API reference documentation for `NowaraJS TOTP` at:
204
-
205
- - [Reference Documentation](https://nowarajs.github.io/totp/)
116
+ Full docs: [nowarajs.github.io/totp](https://nowarajs.github.io/totp/)
206
117
 
207
118
  ## βš–οΈ License
208
119
 
209
- Distributed under the MIT License. See [LICENSE](./LICENSE) for more information.
120
+ MIT - Feel free to use it.
210
121
 
211
122
  ## πŸ“§ Contact
212
123
 
@@ -0,0 +1,11 @@
1
+ // @bun
2
+ // source/enums/totp-error-keys.ts
3
+ var TOTP_ERROR_KEYS = {
4
+ INVALID_ALGORITHM: "nowarajs.totp.error.invalid_algorithm",
5
+ INVALID_BASE32_CHARACTER: "nowarajs.totp.error.invalid_base32_character",
6
+ INVALID_OTP_AUTH_URI: "nowarajs.totp.error.invalid_otp_auth_uri",
7
+ INVALID_SECRET_LENGTH: "nowarajs.totp.error.invalid_secret_length",
8
+ MISSING_SECRET: "nowarajs.totp.error.missing_secret"
9
+ };
10
+
11
+ export { TOTP_ERROR_KEYS };
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  TOTP_ERROR_KEYS
4
- } from "../chunk-4z2jb9kh.js";
4
+ } from "../chunk-sx6rwmqe.js";
5
5
  export {
6
6
  TOTP_ERROR_KEYS
7
7
  };
@@ -1,7 +1,7 @@
1
1
  export declare const TOTP_ERROR_KEYS: {
2
- readonly INVALID_ALGORITHM: "totp.error.invalid_algorithm";
3
- readonly INVALID_BASE32_CHARACTER: "totp.error.invalid_base32_character";
4
- readonly INVALID_OTP_AUTH_URI: "totp.error.invalid_otp_auth_uri";
5
- readonly INVALID_SECRET_LENGTH: "totp.error.invalid_secret_length";
6
- readonly MISSING_SECRET: "totp.error.missing_secret";
2
+ readonly INVALID_ALGORITHM: "nowarajs.totp.error.invalid_algorithm";
3
+ readonly INVALID_BASE32_CHARACTER: "nowarajs.totp.error.invalid_base32_character";
4
+ readonly INVALID_OTP_AUTH_URI: "nowarajs.totp.error.invalid_otp_auth_uri";
5
+ readonly INVALID_SECRET_LENGTH: "nowarajs.totp.error.invalid_secret_length";
6
+ readonly MISSING_SECRET: "nowarajs.totp.error.missing_secret";
7
7
  };
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-q8z45f9z.js";
7
7
  import {
8
8
  TOTP_ERROR_KEYS
9
- } from "./chunk-4z2jb9kh.js";
9
+ } from "./chunk-sx6rwmqe.js";
10
10
 
11
11
  // source/hotp.ts
12
12
  import { webcrypto } from "crypto";
@@ -20,7 +20,7 @@ var hotp = async (secret, counter, {
20
20
  return dynamicTruncation(hmacArray, digits);
21
21
  };
22
22
  // source/otp-auth-uri.ts
23
- import { BaseError } from "@nowarajs/error";
23
+ import { InternalError } from "@nowarajs/error";
24
24
  var buildOtpAuthUri = ({
25
25
  secretBase32,
26
26
  label,
@@ -45,13 +45,13 @@ var buildOtpAuthUri = ({
45
45
  var parseOtpAuthUri = (uri) => {
46
46
  const url = new URL(uri);
47
47
  if (url.protocol !== "otpauth:")
48
- throw new BaseError(TOTP_ERROR_KEYS.INVALID_OTP_AUTH_URI);
48
+ throw new InternalError(TOTP_ERROR_KEYS.INVALID_OTP_AUTH_URI);
49
49
  if (url.hostname !== "totp")
50
- throw new BaseError(TOTP_ERROR_KEYS.INVALID_OTP_AUTH_URI);
50
+ throw new InternalError(TOTP_ERROR_KEYS.INVALID_OTP_AUTH_URI);
51
51
  const label = decodeURIComponent(url.pathname.slice(1));
52
52
  const secretBase32 = url.searchParams.get("secret");
53
53
  if (!secretBase32)
54
- throw new BaseError(TOTP_ERROR_KEYS.MISSING_SECRET);
54
+ throw new InternalError(TOTP_ERROR_KEYS.MISSING_SECRET);
55
55
  const issuerParam = url.searchParams.get("issuer");
56
56
  const issuer = issuerParam ? decodeURIComponent(issuerParam) : undefined;
57
57
  const algorithm = url.searchParams.get("algorithm") || "SHA-1";
@@ -12,7 +12,7 @@ export declare const buildOtpAuthUri: ({ secretBase32, label, issuer, algorithm,
12
12
  *
13
13
  * @param uri - OTPAuth URI to parse
14
14
  *
15
- * @throws ({@link BaseError}) - if the URI is invalid or missing required parameters
15
+ * @throws ({@link InternalError}) - if the URI is invalid or missing required parameters
16
16
  *
17
17
  * @returns Parsed URI parameters
18
18
  */
@@ -12,7 +12,7 @@ export declare const base32Encode: (input: string | Uint8Array, withPadding?: bo
12
12
  *
13
13
  * @param base32 - Base32 string to decode
14
14
  *
15
- * @throws ({@link BaseError}) - if invalid Base32 character is found
15
+ * @throws ({@link InternalError}) - if invalid Base32 character is found
16
16
  *
17
17
  * @returns Decoded bytes
18
18
  */
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @param length - Number of bytes to generate (default: 20)
5
5
  *
6
- * @throws ({@link BaseError}) if length is not positive
6
+ * @throws ({@link InternalError}) if length is not positive
7
7
  *
8
8
  * @returns Uint8Array containing the random bytes
9
9
  */
@@ -6,10 +6,10 @@ import {
6
6
  } from "../chunk-q8z45f9z.js";
7
7
  import {
8
8
  TOTP_ERROR_KEYS
9
- } from "../chunk-4z2jb9kh.js";
9
+ } from "../chunk-sx6rwmqe.js";
10
10
 
11
11
  // source/utils/base32.ts
12
- import { BaseError } from "@nowarajs/error";
12
+ import { InternalError } from "@nowarajs/error";
13
13
  var BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
14
14
  var base32Encode = (input, withPadding = true) => {
15
15
  let result = "";
@@ -41,7 +41,7 @@ var base32Decode = (base32) => {
41
41
  for (const char of cleanBase32) {
42
42
  const charValue = BASE32_ALPHABET.indexOf(char);
43
43
  if (charValue === -1)
44
- throw new BaseError(TOTP_ERROR_KEYS.INVALID_BASE32_CHARACTER, `Invalid Base32 character: ${char}`);
44
+ throw new InternalError(TOTP_ERROR_KEYS.INVALID_BASE32_CHARACTER, `Invalid Base32 character: ${char}`);
45
45
  value = value << 5 | charValue;
46
46
  bits += 5;
47
47
  if (bits >= 8) {
@@ -52,11 +52,11 @@ var base32Decode = (base32) => {
52
52
  return new Uint8Array(result);
53
53
  };
54
54
  // source/utils/generate-secret-bytes.ts
55
- import { BaseError as BaseError2 } from "@nowarajs/error";
55
+ import { InternalError as InternalError2 } from "@nowarajs/error";
56
56
  import { getRandomValues } from "crypto";
57
57
  var generateSecretBytes = (length = 20) => {
58
58
  if (length <= 0)
59
- throw new BaseError2(TOTP_ERROR_KEYS.INVALID_SECRET_LENGTH);
59
+ throw new InternalError2(TOTP_ERROR_KEYS.INVALID_SECRET_LENGTH);
60
60
  return getRandomValues(new Uint8Array(length));
61
61
  };
62
62
  // source/utils/time-remaining.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowarajs/totp",
3
- "version": "1.1.12",
3
+ "version": "1.2.1",
4
4
  "author": "NowaraJS",
5
5
  "description": "A comprehensive Time-based One-Time Password (TOTP) and HMAC-based One-Time Password (HOTP)",
6
6
  "type": "module",
@@ -22,17 +22,17 @@
22
22
  "test": "bun test --coverage"
23
23
  },
24
24
  "devDependencies": {
25
- "@eslint/js": "^9.38.0",
26
- "@nowarajs/error": "^1.3.8",
27
- "@stylistic/eslint-plugin": "^5.5.0",
28
- "@types/bun": "^1.3.0",
29
- "eslint": "^9.38.0",
30
- "globals": "^16.4.0",
31
- "typescript-eslint": "^8.46.2",
25
+ "@eslint/js": "^9.39.2",
26
+ "@nowarajs/error": "^1.4.0",
27
+ "@stylistic/eslint-plugin": "^5.7.0",
28
+ "@types/bun": "^1.3.5",
29
+ "eslint": "^9.39.2",
30
+ "globals": "^17.0.0",
31
+ "typescript-eslint": "^8.52.0",
32
32
  "typescript": "^5.9.3"
33
33
  },
34
34
  "peerDependencies": {
35
- "@nowarajs/error": "^1.3.8"
35
+ "@nowarajs/error": "^1.4.0"
36
36
  },
37
37
  "exports": {
38
38
  "./enums": "./dist/enums/index.js",
@@ -1,11 +0,0 @@
1
- // @bun
2
- // source/enums/totp-error-keys.ts
3
- var TOTP_ERROR_KEYS = {
4
- INVALID_ALGORITHM: "totp.error.invalid_algorithm",
5
- INVALID_BASE32_CHARACTER: "totp.error.invalid_base32_character",
6
- INVALID_OTP_AUTH_URI: "totp.error.invalid_otp_auth_uri",
7
- INVALID_SECRET_LENGTH: "totp.error.invalid_secret_length",
8
- MISSING_SECRET: "totp.error.missing_secret"
9
- };
10
-
11
- export { TOTP_ERROR_KEYS };