@focura/auth-core 1.1.0 → 1.1.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 +242 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -273,6 +273,248 @@ import { TokenManager, SessionManager, TotpManager, AccountLockout } from "@focu
|
|
|
273
273
|
|
|
274
274
|
These are available but not required for normal application development.
|
|
275
275
|
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Default Configuration
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
import { DEFAULTS } from "@focura/auth-core";
|
|
282
|
+
|
|
283
|
+
// All default values:
|
|
284
|
+
DEFAULTS.keyPrefix; // "focura:"
|
|
285
|
+
DEFAULTS.issuer; // "focura-app"
|
|
286
|
+
DEFAULTS.audience; // "focura-backend"
|
|
287
|
+
DEFAULTS.accessTokenExpiry; // "15m"
|
|
288
|
+
DEFAULTS.refreshTokenExpiry; // "7d"
|
|
289
|
+
DEFAULTS.sseTokenExpiry; // "30s"
|
|
290
|
+
DEFAULTS.maxConcurrentSessions; // 5
|
|
291
|
+
DEFAULTS.lockoutMaxFailures; // 10
|
|
292
|
+
DEFAULTS.lockoutSeconds; // 900 (15 minutes)
|
|
293
|
+
DEFAULTS.lockoutWindowSeconds; // 3600 (1 hour)
|
|
294
|
+
DEFAULTS.inactivityTimeout; // 604800 (7 days)
|
|
295
|
+
DEFAULTS.absoluteTimeout; // 604800 (7 days)
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### resolveConfig
|
|
299
|
+
|
|
300
|
+
Merges your config with defaults. Useful for advanced customization:
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
import { resolveConfig } from "@focura/auth-core";
|
|
304
|
+
|
|
305
|
+
const resolved = resolveConfig({
|
|
306
|
+
redis,
|
|
307
|
+
userStore,
|
|
308
|
+
hmacSecret: "...",
|
|
309
|
+
jwt: { privateKey: "...", publicKey: "..." },
|
|
310
|
+
lockout: { maxFailures: 5 }, // override default 10
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// resolved contains all values with defaults applied
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Error Handling
|
|
319
|
+
|
|
320
|
+
All errors include `code` and `statusCode` for API responses:
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
import {
|
|
324
|
+
UnauthorizedError, // 401, code: UNAUTHORIZED
|
|
325
|
+
TokenExpiredError, // 401, code: TOKEN_EXPIRED
|
|
326
|
+
InvalidTokenError, // 401, code: INVALID_TOKEN
|
|
327
|
+
TokenRevokedError, // 401, code: TOKEN_REVOKED
|
|
328
|
+
SessionHijackError, // 401, code: SESSION_HIJACK_DETECTED
|
|
329
|
+
EmailNotVerifiedError, // 403, code: EMAIL_NOT_VERIFIED
|
|
330
|
+
AccountBannedError, // 403, code: ACCOUNT_BANNED
|
|
331
|
+
ForbiddenError, // 403, code: FORBIDDEN
|
|
332
|
+
BadRequestError, // 400, code: BAD_REQUEST
|
|
333
|
+
ValidationError, // 400, code: VALIDATION_ERROR
|
|
334
|
+
} from "@focura/auth-core";
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Example: Catching errors
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
try {
|
|
341
|
+
const { user, payload } = await auth.verifyToken({ token, ipAddress, userAgent });
|
|
342
|
+
} catch (e) {
|
|
343
|
+
if (e instanceof TokenExpiredError) {
|
|
344
|
+
return res.status(401).json({ error: "Token expired", code: e.code });
|
|
345
|
+
}
|
|
346
|
+
if (e instanceof InvalidTokenError) {
|
|
347
|
+
return res.status(401).json({ error: "Invalid token", code: e.code });
|
|
348
|
+
}
|
|
349
|
+
if (e instanceof SessionHijackError) {
|
|
350
|
+
return res.status(401).json({ error: "Session hijack detected", code: e.code });
|
|
351
|
+
}
|
|
352
|
+
if (e instanceof AccountBannedError) {
|
|
353
|
+
return res.status(403).json({ error: "Account banned", reason: e.message });
|
|
354
|
+
}
|
|
355
|
+
if (e instanceof ValidationError) {
|
|
356
|
+
return res.status(400).json({ error: e.message, details: e.details });
|
|
357
|
+
}
|
|
358
|
+
return res.status(500).json({ error: "Internal server error" });
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### defaultErrors Factory
|
|
363
|
+
|
|
364
|
+
Use as base for custom error classes:
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
import { defaultErrors } from "@focura/auth-core";
|
|
368
|
+
|
|
369
|
+
const customErrors = {
|
|
370
|
+
...defaultErrors,
|
|
371
|
+
UnauthorizedError: (msg) => new MyCustomUnauthorizedError(msg),
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
const auth = new AuthService({ ..., errors: customErrors });
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Input & Result Types
|
|
380
|
+
|
|
381
|
+
All types are exported for TypeScript autocompletion:
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
import type {
|
|
385
|
+
// Token exchange
|
|
386
|
+
ExchangeInput, // { userId, email, role, sessionId, timestamp, signature }
|
|
387
|
+
ExchangeResult, // { accessToken, refreshToken, sseToken, sessionId }
|
|
388
|
+
|
|
389
|
+
// Token verification
|
|
390
|
+
VerifyTokenInput, // { token, ipAddress?, userAgent? }
|
|
391
|
+
VerifyTokenResult, // { user, payload }
|
|
392
|
+
|
|
393
|
+
// Refresh
|
|
394
|
+
RefreshInput, // { refreshToken }
|
|
395
|
+
AuthRefreshResult, // { accessToken, refreshToken, sseToken }
|
|
396
|
+
|
|
397
|
+
// Logout
|
|
398
|
+
LogoutInput, // { userId, sessionId, accessTokenJti?, accessToken?, logoutAll? }
|
|
399
|
+
|
|
400
|
+
// 2FA
|
|
401
|
+
TwoFactorSetupResult, // { secret, uri }
|
|
402
|
+
TwoFactorVerifyInput, // { token, secret }
|
|
403
|
+
|
|
404
|
+
// Tokens
|
|
405
|
+
TokenPayload, // { id, email, role, type, version, jti, sessionId? }
|
|
406
|
+
TokenPair, // { accessToken, refreshToken, accessTokenExpiry, refreshTokenExpiry }
|
|
407
|
+
|
|
408
|
+
// Config sub-types
|
|
409
|
+
TokenConfig, // { privateKey, publicKey, issuer?, audience?, accessTokenExpiry?, ... }
|
|
410
|
+
SessionConfig, // { inactivityTimeout?, absoluteTimeout?, maxConcurrent?, metadataTtl? }
|
|
411
|
+
LockoutConfig, // { maxFailures?, lockoutSeconds?, windowSeconds? }
|
|
412
|
+
|
|
413
|
+
// Adapters
|
|
414
|
+
RedisAdapter,
|
|
415
|
+
RedisPipeline,
|
|
416
|
+
UserStore,
|
|
417
|
+
User,
|
|
418
|
+
CacheAdapter,
|
|
419
|
+
AuditLogger,
|
|
420
|
+
ObservabilitySink,
|
|
421
|
+
ErrorFactory,
|
|
422
|
+
ZodSchema,
|
|
423
|
+
|
|
424
|
+
// Session
|
|
425
|
+
SessionMetadata, // { deviceId, ipAddress, userAgent, location?, lastActivity }
|
|
426
|
+
SessionLifecycle, // { recordCreation, invalidate, isTracked, isInactive }
|
|
427
|
+
DeviceFingerprint, // { userAgent, acceptLanguage, acceptEncoding, ipAddress }
|
|
428
|
+
AuthRequest, // Extended request with user property
|
|
429
|
+
} from "@focura/auth-core";
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## MiddlewareFactory Methods
|
|
435
|
+
|
|
436
|
+
All HTTP middleware and route handlers:
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
import { MiddlewareFactory } from "@focura/auth-core";
|
|
440
|
+
|
|
441
|
+
const factory = new MiddlewareFactory(config);
|
|
442
|
+
|
|
443
|
+
// Route handlers
|
|
444
|
+
factory.createExchangeHandler(); // POST /api/v1/auth/exchange
|
|
445
|
+
factory.createRefreshHandler(); // POST /api/v1/auth/refresh
|
|
446
|
+
factory.createLogoutHandler(); // POST /api/v1/auth/logout
|
|
447
|
+
|
|
448
|
+
// Auth middleware
|
|
449
|
+
factory.createAuthenticateMiddleware(); // Verifies JWT, attaches req.user
|
|
450
|
+
factory.createAuthorizeMiddleware("ADMIN", "MOD"); // Role-based access
|
|
451
|
+
|
|
452
|
+
// Security middleware
|
|
453
|
+
factory.createCsrfMiddleware(); // CSRF token validation
|
|
454
|
+
factory.createRateLimitMiddleware(); // Sliding window rate limiting
|
|
455
|
+
factory.createSessionTimeoutMiddleware(); // Inactivity + absolute timeout
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## Utility Functions
|
|
461
|
+
|
|
462
|
+
Low-level helpers available for advanced use:
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
import {
|
|
466
|
+
generateDeviceFingerprint, // (req) => DeviceFingerprint
|
|
467
|
+
getClientIp, // (req) => string
|
|
468
|
+
isPrivateIp, // (ip) => boolean — checks 10.x, 172.16-31.x, 192.168.x, 127.x
|
|
469
|
+
normalizeUserAgent, // (ua) => string — truncates long user agents
|
|
470
|
+
createSessionMetadata, // (fingerprint, sessionId) => SessionMetadata
|
|
471
|
+
validateSessionBinding, // (metadata, fingerprint, options) => { bound, reason? }
|
|
472
|
+
looksLikeServerToServerRequest, // (req) => boolean
|
|
473
|
+
looksLikeServerToServerUA, // (ua) => boolean
|
|
474
|
+
} from "@focura/auth-core";
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## Audit Event Types
|
|
480
|
+
|
|
481
|
+
50+ event types with severity levels:
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
import { AUDIT_SEVERITY } from "@focura/auth-core";
|
|
485
|
+
|
|
486
|
+
// AUDIT_SEVERITY maps event types to severity:
|
|
487
|
+
// "info" — LOGIN_SUCCESS, LOGOUT, TOKEN_REFRESHED, SESSION_BOUND, etc.
|
|
488
|
+
// "warn" — LOGIN_FAILED, DEVICE_MISMATCH, RATE_LIMIT_EXCEEDED, etc.
|
|
489
|
+
// "critical" — TOKEN_REPLAY_DETECTED, ACCOUNT_LOCKED, MALWARE_DETECTED, etc.
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
```typescript
|
|
493
|
+
import type { AuditEventType, AuditSeverity } from "@focura/auth-core";
|
|
494
|
+
|
|
495
|
+
// AuditEventType — union of all 50+ event strings
|
|
496
|
+
// AuditSeverity — "info" | "warn" | "critical"
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Zod Schemas
|
|
502
|
+
|
|
503
|
+
Validation schemas used internally (available for external use):
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
import { exchangeSchema, refreshSchema, logoutSchema } from "@focura/auth-core";
|
|
507
|
+
|
|
508
|
+
// exchangeSchema validates ExchangeInput
|
|
509
|
+
// refreshSchema validates RefreshInput
|
|
510
|
+
// logoutSchema validates LogoutInput
|
|
511
|
+
|
|
512
|
+
const result = exchangeSchema.safeParse(data);
|
|
513
|
+
if (!result.success) {
|
|
514
|
+
console.log(result.error.issues);
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
276
518
|
## License
|
|
277
519
|
|
|
278
520
|
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@focura/auth-core",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Production-ready authentication core for Express.js backends. Dual-token RS256 JWT architecture with session binding, token rotation, 2FA, account lockout, and audit logging.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|