0
# HOTP (HMAC-based One-Time Password)
1
2
HOTP implements RFC 4226 counter-based one-time passwords. Unlike time-based systems, HOTP uses an incrementing counter that must be synchronized between client and server.
3
4
## Capabilities
5
6
### Token Generation
7
8
Generates tokens based on a secret key and counter value.
9
10
```typescript { .api }
11
/**
12
* Generate an HOTP token using secret and counter
13
* @param secret - Secret key in the configured encoding
14
* @param counter - Counter value (must be synchronized)
15
* @returns Token string with configured digit length
16
*/
17
generate(secret: string, counter: number): string;
18
```
19
20
**Usage Example:**
21
22
```typescript
23
import { hotp } from "otplib";
24
25
const secret = "your-secret-key";
26
let counter = 0;
27
28
// Generate tokens with incrementing counter
29
const token1 = hotp.generate(secret, counter++);
30
const token2 = hotp.generate(secret, counter++);
31
const token3 = hotp.generate(secret, counter++);
32
33
console.log(token1); // "123456"
34
console.log(token2); // "789012"
35
console.log(token3); // "345678"
36
37
// Configure custom options
38
hotp.options = { digits: 8, algorithm: 'sha256' };
39
const longToken = hotp.generate(secret, counter++);
40
console.log(longToken); // "12345678"
41
```
42
43
### Token Verification
44
45
Verifies tokens against a specific counter value. The counter must match exactly.
46
47
```typescript { .api }
48
/**
49
* Verify an HOTP token against secret and counter
50
* @param token - Token to verify
51
* @param secret - Secret key
52
* @param counter - Expected counter value
53
* @returns true if token matches the generated token for this counter
54
*/
55
check(token: string, secret: string, counter: number): boolean;
56
57
/**
58
* Object-based token verification
59
* @param opts - Object containing token, secret, and counter
60
* @returns true if token is valid for the given counter
61
*/
62
verify(opts: { token: string; secret: string; counter: number }): boolean;
63
```
64
65
**Usage Examples:**
66
67
```typescript
68
import { hotp } from "otplib";
69
70
const secret = "your-secret-key";
71
const counter = 5;
72
const token = "123456";
73
74
// Method 1: Direct parameters
75
const isValid = hotp.check(token, secret, counter);
76
77
// Method 2: Object parameters
78
const isValid2 = hotp.verify({ token, secret, counter });
79
80
console.log(isValid); // true or false
81
82
// Counter synchronization example
83
let serverCounter = 0;
84
let maxLookAhead = 10;
85
86
function verifyHotpWithSync(token: string, secret: string): boolean {
87
// Try current counter and look ahead
88
for (let i = 0; i <= maxLookAhead; i++) {
89
if (hotp.check(token, secret, serverCounter + i)) {
90
serverCounter = serverCounter + i + 1; // Update for next token
91
return true;
92
}
93
}
94
return false;
95
}
96
```
97
98
### QR Code URI Generation
99
100
Generate otpauth:// URIs for HOTP setup with counter information.
101
102
```typescript { .api }
103
/**
104
* Generate an otpauth URI for HOTP setup
105
* @param accountName - User identifier
106
* @param issuer - Service name
107
* @param secret - Secret key
108
* @param counter - Initial counter value
109
* @returns otpauth://hotp/ URI string
110
*/
111
keyuri(accountName: string, issuer: string, secret: string, counter: number): string;
112
```
113
114
**Usage Example:**
115
116
```typescript
117
import { hotp } from "otplib";
118
119
const secret = "your-secret-key";
120
const user = "user@example.com";
121
const service = "My Service";
122
const initialCounter = 0;
123
124
const otpauth = hotp.keyuri(user, service, secret, initialCounter);
125
console.log(otpauth);
126
// "otpauth://hotp/My%20Service:user@example.com?secret=your-secret-key&counter=0&issuer=My%20Service"
127
```
128
129
### Configuration Management
130
131
Manage HOTP-specific configuration options.
132
133
```typescript { .api }
134
/**
135
* Get/set configuration options
136
*/
137
options: Partial<HOTPOptions>;
138
139
/**
140
* Reset options to default values
141
*/
142
resetOptions(): void;
143
144
/**
145
* Get all options with defaults applied
146
* @returns Complete options object
147
*/
148
allOptions(): Readonly<HOTPOptions>;
149
150
/**
151
* Create new instance with custom defaults
152
* @param defaultOptions - Custom default options
153
* @returns New HOTP instance
154
*/
155
create(defaultOptions?: Partial<HOTPOptions>): HOTP;
156
```
157
158
**Usage Examples:**
159
160
```typescript
161
import { hotp } from "otplib";
162
163
// Configure options
164
hotp.options = {
165
digits: 8, // 8-digit tokens
166
algorithm: 'sha256', // SHA-256 HMAC
167
encoding: 'hex' // Hex-encoded secrets
168
};
169
170
// Create instance with custom defaults
171
const customHotp = hotp.create({
172
digits: 6,
173
algorithm: 'sha1',
174
encoding: 'ascii'
175
});
176
177
// Reset to library defaults
178
hotp.resetOptions();
179
180
// View complete configuration
181
const config = hotp.allOptions();
182
console.log(config.digits); // 6
183
console.log(config.algorithm); // 'sha1'
184
```
185
186
## Counter Management Patterns
187
188
### Simple Counter Tracking
189
190
```typescript
191
import { hotp } from "otplib";
192
193
class HotpService {
194
private counters = new Map<string, number>();
195
196
generateToken(userId: string, secret: string): string {
197
const counter = this.getCounter(userId);
198
const token = hotp.generate(secret, counter);
199
this.incrementCounter(userId);
200
return token;
201
}
202
203
verifyToken(userId: string, secret: string, token: string): boolean {
204
const counter = this.getCounter(userId);
205
return hotp.check(token, secret, counter);
206
}
207
208
private getCounter(userId: string): number {
209
return this.counters.get(userId) || 0;
210
}
211
212
private incrementCounter(userId: string): void {
213
const current = this.getCounter(userId);
214
this.counters.set(userId, current + 1);
215
}
216
}
217
```
218
219
### Counter Synchronization with Look-ahead
220
221
```typescript
222
import { hotp } from "otplib";
223
224
class HotpSyncService {
225
private counters = new Map<string, number>();
226
private readonly LOOK_AHEAD_WINDOW = 10;
227
228
verifyTokenWithSync(userId: string, secret: string, token: string): boolean {
229
const baseCounter = this.getCounter(userId);
230
231
// Try current counter and look ahead
232
for (let i = 0; i <= this.LOOK_AHEAD_WINDOW; i++) {
233
const testCounter = baseCounter + i;
234
if (hotp.check(token, secret, testCounter)) {
235
// Update counter to one past the successful counter
236
this.setCounter(userId, testCounter + 1);
237
return true;
238
}
239
}
240
241
return false;
242
}
243
244
private getCounter(userId: string): number {
245
return this.counters.get(userId) || 0;
246
}
247
248
private setCounter(userId: string, counter: number): void {
249
this.counters.set(userId, counter);
250
}
251
}
252
```
253
254
### Hardware Token Simulation
255
256
```typescript
257
import { hotp } from "otplib";
258
259
class HardwareTokenSimulator {
260
private counter: number = 0;
261
262
constructor(private secret: string) {}
263
264
// Simulate pressing the button on a hardware token
265
pressButton(): string {
266
const token = hotp.generate(this.secret, this.counter);
267
this.counter++;
268
return token;
269
}
270
271
// Reset counter (usually requires physical reset)
272
resetCounter(newCounter: number = 0): void {
273
this.counter = newCounter;
274
}
275
276
getCurrentCounter(): number {
277
return this.counter;
278
}
279
}
280
281
// Usage
282
const token = new HardwareTokenSimulator("shared-secret");
283
const firstToken = token.pressButton(); // Uses counter 0
284
const secondToken = token.pressButton(); // Uses counter 1
285
```
286
287
## Types
288
289
```typescript { .api }
290
interface HOTPOptions {
291
/** HMAC algorithm to use */
292
algorithm: 'sha1' | 'sha256' | 'sha512';
293
/** Number of digits in generated token */
294
digits: number;
295
/** Encoding format of the secret key */
296
encoding: 'ascii' | 'base64' | 'hex' | 'latin1' | 'utf8';
297
/** Function to create HMAC digest */
298
createDigest: (algorithm: string, key: string, data: string) => string;
299
/** Function to create HMAC key from secret */
300
createHmacKey: (algorithm: string, secret: string, encoding: string) => string;
301
/** Pre-computed digest (advanced usage) */
302
digest?: string;
303
}
304
```