@ooneex/jwt 0.0.1 → 0.0.5
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 +505 -0
- package/package.json +14 -6
- package/dist/ooneex-jwt-0.0.1.tgz +0 -0
package/README.md
CHANGED
|
@@ -1 +1,506 @@
|
|
|
1
1
|
# @ooneex/jwt
|
|
2
|
+
|
|
3
|
+
A JWT (JSON Web Token) generation and validation library for TypeScript applications using the JOSE library. This package provides secure token creation, verification, and payload extraction with full TypeScript support and expiration time helpers.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+

|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
✅ **JOSE Powered** - Built on the robust JOSE library for secure JWT operations
|
|
14
|
+
|
|
15
|
+
✅ **Token Generation** - Create signed JWT tokens with custom payloads and headers
|
|
16
|
+
|
|
17
|
+
✅ **Token Validation** - Verify token signatures and expiration
|
|
18
|
+
|
|
19
|
+
✅ **Payload Extraction** - Decode and retrieve payload data from tokens
|
|
20
|
+
|
|
21
|
+
✅ **Flexible Expiration** - Support for various expiration formats (seconds, minutes, hours, days)
|
|
22
|
+
|
|
23
|
+
✅ **Type-Safe** - Full TypeScript support with generic payload types
|
|
24
|
+
|
|
25
|
+
✅ **Environment Config** - Automatic secret loading from environment variables
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
### Bun
|
|
30
|
+
```bash
|
|
31
|
+
bun add @ooneex/jwt
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### pnpm
|
|
35
|
+
```bash
|
|
36
|
+
pnpm add @ooneex/jwt
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Yarn
|
|
40
|
+
```bash
|
|
41
|
+
yarn add @ooneex/jwt
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### npm
|
|
45
|
+
```bash
|
|
46
|
+
npm install @ooneex/jwt
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
### Basic Token Generation
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { Jwt } from '@ooneex/jwt';
|
|
55
|
+
|
|
56
|
+
const jwt = new Jwt('your-secret-key');
|
|
57
|
+
|
|
58
|
+
// Create a simple token
|
|
59
|
+
const token = await jwt.create({
|
|
60
|
+
payload: {
|
|
61
|
+
sub: 'user-123',
|
|
62
|
+
exp: '1h' // Expires in 1 hour
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
console.log(token);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Token with Custom Payload
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { Jwt } from '@ooneex/jwt';
|
|
73
|
+
|
|
74
|
+
const jwt = new Jwt();
|
|
75
|
+
|
|
76
|
+
interface UserPayload {
|
|
77
|
+
userId: string;
|
|
78
|
+
role: string;
|
|
79
|
+
permissions: string[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const token = await jwt.create<UserPayload>({
|
|
83
|
+
payload: {
|
|
84
|
+
sub: 'user-123',
|
|
85
|
+
iss: 'my-app',
|
|
86
|
+
aud: 'my-api',
|
|
87
|
+
exp: '24h',
|
|
88
|
+
iat: new Date(),
|
|
89
|
+
userId: 'usr_abc123',
|
|
90
|
+
role: 'admin',
|
|
91
|
+
permissions: ['read', 'write', 'delete']
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Token Validation
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { Jwt } from '@ooneex/jwt';
|
|
100
|
+
|
|
101
|
+
const jwt = new Jwt();
|
|
102
|
+
|
|
103
|
+
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
|
|
104
|
+
|
|
105
|
+
// Check if token is valid
|
|
106
|
+
const isValid = await jwt.isValid(token);
|
|
107
|
+
|
|
108
|
+
if (isValid) {
|
|
109
|
+
console.log('Token is valid');
|
|
110
|
+
} else {
|
|
111
|
+
console.log('Token is invalid or expired');
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Extracting Payload
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { Jwt } from '@ooneex/jwt';
|
|
119
|
+
|
|
120
|
+
const jwt = new Jwt();
|
|
121
|
+
|
|
122
|
+
interface UserPayload {
|
|
123
|
+
userId: string;
|
|
124
|
+
role: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
|
|
128
|
+
|
|
129
|
+
// Get payload without verification (decodes only)
|
|
130
|
+
const payload = jwt.getPayload<UserPayload>(token);
|
|
131
|
+
|
|
132
|
+
console.log(payload.userId); // "usr_abc123"
|
|
133
|
+
console.log(payload.role); // "admin"
|
|
134
|
+
console.log(payload.exp); // Expiration timestamp
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Extracting Header
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { Jwt } from '@ooneex/jwt';
|
|
141
|
+
|
|
142
|
+
const jwt = new Jwt();
|
|
143
|
+
|
|
144
|
+
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
|
|
145
|
+
|
|
146
|
+
const header = jwt.getHeader(token);
|
|
147
|
+
|
|
148
|
+
console.log(header.alg); // "HS256"
|
|
149
|
+
console.log(header.typ); // "JWT"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Using Environment Variables
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { Jwt } from '@ooneex/jwt';
|
|
156
|
+
|
|
157
|
+
// Automatically uses JWT_SECRET environment variable
|
|
158
|
+
const jwt = new Jwt();
|
|
159
|
+
|
|
160
|
+
const token = await jwt.create({
|
|
161
|
+
payload: {
|
|
162
|
+
sub: 'user-123',
|
|
163
|
+
exp: '1h'
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Environment Variables:**
|
|
169
|
+
- `JWT_SECRET` - The secret key used for signing and verifying tokens
|
|
170
|
+
|
|
171
|
+
## API Reference
|
|
172
|
+
|
|
173
|
+
### Classes
|
|
174
|
+
|
|
175
|
+
#### `Jwt`
|
|
176
|
+
|
|
177
|
+
Main class for JWT operations.
|
|
178
|
+
|
|
179
|
+
**Constructor:**
|
|
180
|
+
```typescript
|
|
181
|
+
new Jwt(secret?: string)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Parameters:**
|
|
185
|
+
- `secret` - Optional secret key. If not provided, uses `JWT_SECRET` environment variable
|
|
186
|
+
|
|
187
|
+
**Throws:** `JwtException` if no secret is provided or found in environment
|
|
188
|
+
|
|
189
|
+
**Methods:**
|
|
190
|
+
|
|
191
|
+
##### `create<T>(config?: CreateConfig): Promise<string>`
|
|
192
|
+
|
|
193
|
+
Creates a new signed JWT token.
|
|
194
|
+
|
|
195
|
+
**Parameters:**
|
|
196
|
+
- `config.payload` - The JWT payload including standard and custom claims
|
|
197
|
+
- `config.header` - Optional custom header parameters
|
|
198
|
+
|
|
199
|
+
**Returns:** Promise resolving to the signed token string
|
|
200
|
+
|
|
201
|
+
**Example:**
|
|
202
|
+
```typescript
|
|
203
|
+
const token = await jwt.create({
|
|
204
|
+
payload: {
|
|
205
|
+
sub: 'user-123',
|
|
206
|
+
iss: 'my-app',
|
|
207
|
+
exp: '2h',
|
|
208
|
+
customClaim: 'value'
|
|
209
|
+
},
|
|
210
|
+
header: {
|
|
211
|
+
kid: 'key-id-123'
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
##### `isValid(token: string): Promise<boolean>`
|
|
217
|
+
|
|
218
|
+
Verifies if a token is valid (signature and expiration).
|
|
219
|
+
|
|
220
|
+
**Parameters:**
|
|
221
|
+
- `token` - The JWT token to verify
|
|
222
|
+
|
|
223
|
+
**Returns:** Promise resolving to `true` if valid, `false` otherwise
|
|
224
|
+
|
|
225
|
+
**Example:**
|
|
226
|
+
```typescript
|
|
227
|
+
const isValid = await jwt.isValid(token);
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
##### `getPayload<T>(token: string): JwtPayloadType<T>`
|
|
231
|
+
|
|
232
|
+
Decodes and returns the token payload (without verification).
|
|
233
|
+
|
|
234
|
+
**Parameters:**
|
|
235
|
+
- `token` - The JWT token to decode
|
|
236
|
+
|
|
237
|
+
**Returns:** The decoded payload
|
|
238
|
+
|
|
239
|
+
**Example:**
|
|
240
|
+
```typescript
|
|
241
|
+
interface MyPayload {
|
|
242
|
+
userId: string;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const payload = jwt.getPayload<MyPayload>(token);
|
|
246
|
+
console.log(payload.userId);
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
##### `getHeader(token: string): JWTHeaderParameters`
|
|
250
|
+
|
|
251
|
+
Decodes and returns the token header.
|
|
252
|
+
|
|
253
|
+
**Parameters:**
|
|
254
|
+
- `token` - The JWT token to decode
|
|
255
|
+
|
|
256
|
+
**Returns:** The decoded header
|
|
257
|
+
|
|
258
|
+
##### `getSecret(): Uint8Array<ArrayBuffer>`
|
|
259
|
+
|
|
260
|
+
Returns the encoded secret used for signing.
|
|
261
|
+
|
|
262
|
+
**Returns:** The secret as a Uint8Array
|
|
263
|
+
|
|
264
|
+
### Types
|
|
265
|
+
|
|
266
|
+
#### `JwtExpiresInType`
|
|
267
|
+
|
|
268
|
+
Expiration time format strings.
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
type JwtExpiresInType =
|
|
272
|
+
| `${number}s` // seconds
|
|
273
|
+
| `${number}m` // minutes
|
|
274
|
+
| `${number}h` // hours
|
|
275
|
+
| `${number}d` // days
|
|
276
|
+
| `${number}w` // weeks
|
|
277
|
+
| `${number}y`; // years
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
**Examples:**
|
|
281
|
+
- `'30s'` - 30 seconds
|
|
282
|
+
- `'15m'` - 15 minutes
|
|
283
|
+
- `'2h'` - 2 hours
|
|
284
|
+
- `'7d'` - 7 days
|
|
285
|
+
- `'1w'` - 1 week
|
|
286
|
+
- `'1y'` - 1 year
|
|
287
|
+
|
|
288
|
+
#### `JwtDefaultPayloadType`
|
|
289
|
+
|
|
290
|
+
Standard JWT claims.
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
type JwtDefaultPayloadType = {
|
|
294
|
+
iss?: string; // Issuer
|
|
295
|
+
sub?: string; // Subject
|
|
296
|
+
aud?: string | string[]; // Audience
|
|
297
|
+
jti?: string; // JWT ID
|
|
298
|
+
nbf?: number | string | Date; // Not Before
|
|
299
|
+
exp?: number | JwtExpiresInType | Date; // Expiration
|
|
300
|
+
iat?: number | string | Date; // Issued At
|
|
301
|
+
};
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
#### `JwtPayloadType<T>`
|
|
305
|
+
|
|
306
|
+
Combined payload type with custom claims.
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
type JwtPayloadType<T> = JwtDefaultPayloadType & T;
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
#### `IJwt`
|
|
313
|
+
|
|
314
|
+
Interface for JWT implementations.
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
interface IJwt {
|
|
318
|
+
create: <T extends Record<string, unknown>>(config?: {
|
|
319
|
+
payload?: JwtDefaultPayloadType & JwtPayloadType<T>;
|
|
320
|
+
header?: JWTHeaderParameters;
|
|
321
|
+
}) => Promise<string>;
|
|
322
|
+
getPayload: <T extends Record<string, unknown>>(token: string) => JwtPayloadType<T>;
|
|
323
|
+
getHeader: (token: string) => JWTHeaderParameters;
|
|
324
|
+
isValid: (token: string) => Promise<boolean> | boolean;
|
|
325
|
+
getSecret: () => Uint8Array<ArrayBuffer>;
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Advanced Usage
|
|
330
|
+
|
|
331
|
+
### Authentication Flow
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
import { Jwt, JwtException } from '@ooneex/jwt';
|
|
335
|
+
|
|
336
|
+
const jwt = new Jwt();
|
|
337
|
+
|
|
338
|
+
// Login - Generate tokens
|
|
339
|
+
async function login(userId: string, role: string) {
|
|
340
|
+
const accessToken = await jwt.create({
|
|
341
|
+
payload: {
|
|
342
|
+
sub: userId,
|
|
343
|
+
role,
|
|
344
|
+
exp: '15m' // Short-lived access token
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const refreshToken = await jwt.create({
|
|
349
|
+
payload: {
|
|
350
|
+
sub: userId,
|
|
351
|
+
type: 'refresh',
|
|
352
|
+
exp: '7d' // Long-lived refresh token
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
return { accessToken, refreshToken };
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Verify and extract user from token
|
|
360
|
+
async function authenticateRequest(token: string) {
|
|
361
|
+
const isValid = await jwt.isValid(token);
|
|
362
|
+
|
|
363
|
+
if (!isValid) {
|
|
364
|
+
throw new Error('Invalid or expired token');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const payload = jwt.getPayload<{ role: string }>(token);
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
userId: payload.sub,
|
|
371
|
+
role: payload.role
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Refresh Token Implementation
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
import { Jwt } from '@ooneex/jwt';
|
|
380
|
+
|
|
381
|
+
const jwt = new Jwt();
|
|
382
|
+
|
|
383
|
+
async function refreshAccessToken(refreshToken: string) {
|
|
384
|
+
const isValid = await jwt.isValid(refreshToken);
|
|
385
|
+
|
|
386
|
+
if (!isValid) {
|
|
387
|
+
throw new Error('Invalid refresh token');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const payload = jwt.getPayload<{ type: string }>(refreshToken);
|
|
391
|
+
|
|
392
|
+
if (payload.type !== 'refresh') {
|
|
393
|
+
throw new Error('Not a refresh token');
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Generate new access token
|
|
397
|
+
const newAccessToken = await jwt.create({
|
|
398
|
+
payload: {
|
|
399
|
+
sub: payload.sub,
|
|
400
|
+
exp: '15m'
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
return newAccessToken;
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Error Handling
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
import { Jwt, JwtException } from '@ooneex/jwt';
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
const jwt = new Jwt(); // Will throw if JWT_SECRET is not set
|
|
415
|
+
|
|
416
|
+
const token = await jwt.create({
|
|
417
|
+
payload: { sub: 'user-123' }
|
|
418
|
+
});
|
|
419
|
+
} catch (error) {
|
|
420
|
+
if (error instanceof JwtException) {
|
|
421
|
+
console.error('JWT Error:', error.message);
|
|
422
|
+
console.error('Status:', error.status);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Middleware Integration
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
import { Jwt } from '@ooneex/jwt';
|
|
431
|
+
import type { IMiddleware, ContextType } from '@ooneex/middleware';
|
|
432
|
+
|
|
433
|
+
class AuthMiddleware implements IMiddleware {
|
|
434
|
+
private readonly jwt = new Jwt();
|
|
435
|
+
|
|
436
|
+
public async handle(context: ContextType): Promise<ContextType> {
|
|
437
|
+
const authHeader = context.header.get('Authorization');
|
|
438
|
+
|
|
439
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
440
|
+
return context.response.exception('Missing authorization header', {
|
|
441
|
+
status: 401
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const token = authHeader.substring(7);
|
|
446
|
+
const isValid = await this.jwt.isValid(token);
|
|
447
|
+
|
|
448
|
+
if (!isValid) {
|
|
449
|
+
return context.response.exception('Invalid or expired token', {
|
|
450
|
+
status: 401
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const payload = this.jwt.getPayload<{ role: string }>(token);
|
|
455
|
+
context.user = { id: payload.sub, role: payload.role };
|
|
456
|
+
|
|
457
|
+
return context;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Custom Header Parameters
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
import { Jwt } from '@ooneex/jwt';
|
|
466
|
+
|
|
467
|
+
const jwt = new Jwt();
|
|
468
|
+
|
|
469
|
+
const token = await jwt.create({
|
|
470
|
+
payload: {
|
|
471
|
+
sub: 'user-123',
|
|
472
|
+
exp: '1h'
|
|
473
|
+
},
|
|
474
|
+
header: {
|
|
475
|
+
kid: 'my-key-id', // Key ID
|
|
476
|
+
typ: 'JWT', // Type
|
|
477
|
+
cty: 'application/json' // Content Type
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
## License
|
|
483
|
+
|
|
484
|
+
This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
|
|
485
|
+
|
|
486
|
+
## Contributing
|
|
487
|
+
|
|
488
|
+
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
|
|
489
|
+
|
|
490
|
+
### Development Setup
|
|
491
|
+
|
|
492
|
+
1. Clone the repository
|
|
493
|
+
2. Install dependencies: `bun install`
|
|
494
|
+
3. Run tests: `bun run test`
|
|
495
|
+
4. Build the project: `bun run build`
|
|
496
|
+
|
|
497
|
+
### Guidelines
|
|
498
|
+
|
|
499
|
+
- Write tests for new features
|
|
500
|
+
- Follow the existing code style
|
|
501
|
+
- Update documentation for API changes
|
|
502
|
+
- Ensure all tests pass before submitting PR
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
Made with ❤️ by the Ooneex team
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ooneex/jwt",
|
|
3
|
-
"description": "",
|
|
4
|
-
"version": "0.0.
|
|
3
|
+
"description": "JWT token generation and validation using the JOSE library for secure authentication",
|
|
4
|
+
"version": "0.0.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist",
|
|
@@ -24,9 +24,8 @@
|
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "bunup",
|
|
26
26
|
"lint": "tsgo --noEmit && bunx biome lint",
|
|
27
|
-
"publish
|
|
28
|
-
"
|
|
29
|
-
"publish:dry": "bun publish --dry-run"
|
|
27
|
+
"npm:publish": "bun publish --tolerate-republish --access public",
|
|
28
|
+
"test": "bun test tests"
|
|
30
29
|
},
|
|
31
30
|
"dependencies": {
|
|
32
31
|
"@ooneex/exception": "0.0.1",
|
|
@@ -34,5 +33,14 @@
|
|
|
34
33
|
"jose": "^6.1.2"
|
|
35
34
|
},
|
|
36
35
|
"devDependencies": {},
|
|
37
|
-
"
|
|
36
|
+
"keywords": [
|
|
37
|
+
"authentication",
|
|
38
|
+
"bun",
|
|
39
|
+
"jose",
|
|
40
|
+
"json-web-token",
|
|
41
|
+
"jwt",
|
|
42
|
+
"ooneex",
|
|
43
|
+
"token",
|
|
44
|
+
"typescript"
|
|
45
|
+
]
|
|
38
46
|
}
|
|
Binary file
|