@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 +61 -150
- package/dist/chunk-sx6rwmqe.js +11 -0
- package/dist/enums/index.js +1 -1
- package/dist/enums/totp-error-keys.d.ts +5 -5
- package/dist/index.js +5 -5
- package/dist/otp-auth-uri.d.ts +1 -1
- package/dist/utils/base32.d.ts +1 -1
- package/dist/utils/generate-secret-bytes.d.ts +1 -1
- package/dist/utils/index.js +5 -5
- package/package.json +9 -9
- package/dist/chunk-4z2jb9kh.js +0 -11
package/README.md
CHANGED
|
@@ -1,41 +1,32 @@
|
|
|
1
|
-
# π NowaraJS
|
|
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
|
-
- [
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
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
|
-
###
|
|
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);
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
51
|
+
algorithm: 'SHA-1',
|
|
52
|
+
digits: 6,
|
|
53
|
+
period: 30
|
|
62
54
|
});
|
|
63
55
|
|
|
64
|
-
console.log('
|
|
56
|
+
console.log('Your code:', code); // e.g., "847263"
|
|
65
57
|
```
|
|
66
58
|
|
|
67
|
-
###
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
77
|
+
### Generate a QR Code URI
|
|
88
78
|
|
|
89
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
101
|
+
### HOTP (Counter-Based)
|
|
125
102
|
|
|
126
|
-
|
|
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
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 };
|
package/dist/enums/index.js
CHANGED
|
@@ -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-
|
|
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 {
|
|
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
|
|
48
|
+
throw new InternalError(TOTP_ERROR_KEYS.INVALID_OTP_AUTH_URI);
|
|
49
49
|
if (url.hostname !== "totp")
|
|
50
|
-
throw new
|
|
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
|
|
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";
|
package/dist/otp-auth-uri.d.ts
CHANGED
|
@@ -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
|
|
15
|
+
* @throws ({@link InternalError}) - if the URI is invalid or missing required parameters
|
|
16
16
|
*
|
|
17
17
|
* @returns Parsed URI parameters
|
|
18
18
|
*/
|
package/dist/utils/base32.d.ts
CHANGED
|
@@ -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
|
|
15
|
+
* @throws ({@link InternalError}) - if invalid Base32 character is found
|
|
16
16
|
*
|
|
17
17
|
* @returns Decoded bytes
|
|
18
18
|
*/
|
package/dist/utils/index.js
CHANGED
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
} from "../chunk-q8z45f9z.js";
|
|
7
7
|
import {
|
|
8
8
|
TOTP_ERROR_KEYS
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-sx6rwmqe.js";
|
|
10
10
|
|
|
11
11
|
// source/utils/base32.ts
|
|
12
|
-
import {
|
|
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
|
|
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 {
|
|
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
|
|
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
|
|
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.
|
|
26
|
-
"@nowarajs/error": "^1.
|
|
27
|
-
"@stylistic/eslint-plugin": "^5.
|
|
28
|
-
"@types/bun": "^1.3.
|
|
29
|
-
"eslint": "^9.
|
|
30
|
-
"globals": "^
|
|
31
|
-
"typescript-eslint": "^8.
|
|
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.
|
|
35
|
+
"@nowarajs/error": "^1.4.0"
|
|
36
36
|
},
|
|
37
37
|
"exports": {
|
|
38
38
|
"./enums": "./dist/enums/index.js",
|
package/dist/chunk-4z2jb9kh.js
DELETED
|
@@ -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 };
|