najm-auth 1.1.38 → 1.1.41
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 +14 -9
- package/dist/index.d.ts +176 -49
- package/dist/index.js +702 -597
- package/dist/schema/pg.d.ts +2 -2
- package/dist/schema/pg.js +3 -2
- package/dist/schema/sqlite.d.ts +2 -2
- package/dist/schema/sqlite.js +3 -2
- package/package.json +10 -18
- package/dist/schema/mysql.d.ts +0 -1535
- package/dist/schema/mysql.js +0 -85
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Production-ready authentication and authorization library for the Najm framework
|
|
|
9
9
|
- ✅ Permission-based access control (PBAC) with wildcards
|
|
10
10
|
- ✅ Row-level ownership scoping for multi-tenant apps
|
|
11
11
|
- ✅ Built-in password reset flow with email support
|
|
12
|
-
- ✅ Multi-dialect support (PostgreSQL, SQLite
|
|
12
|
+
- ✅ Multi-dialect support (PostgreSQL, SQLite)
|
|
13
13
|
- ✅ Type-safe decorators with TypeScript
|
|
14
14
|
- ✅ Rate limiting on auth endpoints
|
|
15
15
|
- ✅ Internationalization (i18n) for all messages
|
|
@@ -103,7 +103,7 @@ FRONTEND_URL=https://app.example.com
|
|
|
103
103
|
```typescript
|
|
104
104
|
auth({
|
|
105
105
|
// Database
|
|
106
|
-
dialect?: 'pg' | 'sqlite'
|
|
106
|
+
dialect?: 'pg' | 'sqlite' // Default: 'pg' (RETURNING-capable engines only)
|
|
107
107
|
schema?: AuthSchema // Override dialect schema
|
|
108
108
|
|
|
109
109
|
// JWT
|
|
@@ -123,6 +123,7 @@ auth({
|
|
|
123
123
|
|
|
124
124
|
// Registration
|
|
125
125
|
defaultRole?: string | null // Auto-assign role to new users
|
|
126
|
+
bcryptRounds?: number // Default: 10 (valid: 4-31)
|
|
126
127
|
|
|
127
128
|
// Frontend
|
|
128
129
|
frontendUrl?: string // Password reset link base URL
|
|
@@ -155,7 +156,7 @@ All routes are prefixed with `/auth` and auto-registered by the plugin.
|
|
|
155
156
|
|
|
156
157
|
| Method | Path | Description |
|
|
157
158
|
|--------|------|-------------|
|
|
158
|
-
| `GET` | `/users` | List
|
|
159
|
+
| `GET` | `/users?limit=50&offset=0` | List users (limit 1-100) |
|
|
159
160
|
| `GET` | `/users/:id` | Get user by ID |
|
|
160
161
|
| `POST` | `/users` | Create new user |
|
|
161
162
|
| `PUT` | `/users/:id` | Update user |
|
|
@@ -510,8 +511,8 @@ Auth routes have built-in rate limiting to prevent brute force attacks.
|
|
|
510
511
|
|-------|-------|--------|--------------|
|
|
511
512
|
| `POST /auth/register` | 5 | 15 minutes | IP |
|
|
512
513
|
| `POST /auth/login` | 5 | 15 minutes | IP |
|
|
513
|
-
| `
|
|
514
|
-
| `
|
|
514
|
+
| `POST /auth/refresh` | 15 | 15 minutes | Cookie fingerprint |
|
|
515
|
+
| `POST /auth/logout` | 10 | 15 minutes | User ID |
|
|
515
516
|
| `GET /auth/me` | 30 | 1 minute | User ID |
|
|
516
517
|
| `POST /auth/forgot-password` | 3 | 15 minutes | IP |
|
|
517
518
|
| `POST /auth/reset-password` | 5 | 15 minutes | IP |
|
|
@@ -592,15 +593,18 @@ async resetPassword(token: string, newPassword: string) {
|
|
|
592
593
|
|
|
593
594
|
### Session Management
|
|
594
595
|
|
|
595
|
-
-
|
|
596
|
-
-
|
|
596
|
+
- Sessions are multi-device: the token table stores one refresh row per login session (keyed by a unique `tokenFamily`), so a user can stay logged in on several devices at once. Logout and rotation are scoped to the current session; password change/reset revoke every session
|
|
597
|
+
- A stale refresh token presented after the 120-second rotation grace window revokes only that session's family as reuse protection
|
|
598
|
+
- The signed session cookie is accepted for up to its configured TTL (5 minutes by default) without a database or revocation-cache read
|
|
597
599
|
- Use `@RateLimit` on logout for DDoS protection
|
|
598
600
|
|
|
599
601
|
### Token Blacklist
|
|
600
602
|
|
|
601
603
|
- Built-in cache-based blacklist for immediate revocation
|
|
602
604
|
- Supports Redis via `cache()` plugin configuration
|
|
603
|
-
- Default: in-memory store (
|
|
605
|
+
- Default: in-memory store (development/single-process only; entries are lost on restart)
|
|
606
|
+
- Use Redis in production when immediate revocation must survive restarts or propagate across instances
|
|
607
|
+
- Session-version revocation keys are cache-backed and TTL-bound to active access tokens
|
|
604
608
|
|
|
605
609
|
### Timing Attack Prevention
|
|
606
610
|
|
|
@@ -635,7 +639,8 @@ Test files include:
|
|
|
635
639
|
- ✅ Set `FRONTEND_URL` environment variable
|
|
636
640
|
- ✅ Enable HTTPS in production
|
|
637
641
|
- ✅ Store secrets in environment variables (never in code)
|
|
638
|
-
- ✅ Use Redis for token blacklist in distributed systems
|
|
642
|
+
- ✅ Use Redis for token blacklist/session-version revocation in production and distributed systems
|
|
643
|
+
- ✅ Trust forwarded IP headers only behind a known proxy; otherwise provide a custom rate-limit key generator
|
|
639
644
|
- ✅ Enable rate limiting on all auth routes
|
|
640
645
|
- ✅ Log authentication events for audit trails
|
|
641
646
|
- ✅ Test ownership scoping rules with multi-user scenarios
|
package/dist/index.d.ts
CHANGED
|
@@ -65,6 +65,8 @@ interface AuthConfig {
|
|
|
65
65
|
registrationMode: 'active' | 'pending';
|
|
66
66
|
/** Per-account lockout settings */
|
|
67
67
|
lockout: LockoutConfig;
|
|
68
|
+
/** Bcrypt work factor (default: 10) */
|
|
69
|
+
bcryptRounds: number;
|
|
68
70
|
/** Session cookie cache settings */
|
|
69
71
|
session: SessionCookieConfig;
|
|
70
72
|
}
|
|
@@ -74,7 +76,7 @@ interface AuthConfig {
|
|
|
74
76
|
*/
|
|
75
77
|
/**
|
|
76
78
|
* Auth schema shape (dialect-agnostic)
|
|
77
|
-
* Import from 'najm-auth/pg'
|
|
79
|
+
* Import from 'najm-auth/pg' or 'najm-auth/sqlite'.
|
|
78
80
|
*/
|
|
79
81
|
interface AuthSchema {
|
|
80
82
|
users: any;
|
|
@@ -84,9 +86,17 @@ interface AuthSchema {
|
|
|
84
86
|
rolePermissions: any;
|
|
85
87
|
}
|
|
86
88
|
type AuthPluginConfig = {
|
|
87
|
-
/**
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Database dialect (default: 'pg'). Auto-selects the correct schema.
|
|
91
|
+
* Only RETURNING-capable engines are supported — the auth data layer
|
|
92
|
+
* relies on `.returning()` for every write. MySQL is not supported.
|
|
93
|
+
*/
|
|
94
|
+
dialect?: 'pg' | 'sqlite';
|
|
95
|
+
/**
|
|
96
|
+
* Database schema tables (optional, overrides dialect). Use authSchema
|
|
97
|
+
* from 'najm-auth/pg' or 'najm-auth/sqlite'. A custom schema must still be
|
|
98
|
+
* backed by a RETURNING-capable engine (Postgres or SQLite).
|
|
99
|
+
*/
|
|
90
100
|
schema?: AuthSchema;
|
|
91
101
|
/** JWT configuration (secrets can be set via env vars) */
|
|
92
102
|
jwt?: Partial<JwtConfig>;
|
|
@@ -104,6 +114,8 @@ type AuthPluginConfig = {
|
|
|
104
114
|
registrationMode?: 'active' | 'pending';
|
|
105
115
|
/** Per-account lockout settings */
|
|
106
116
|
lockout?: Partial<LockoutConfig>;
|
|
117
|
+
/** Bcrypt work factor (default: 10, valid range: 4-31) */
|
|
118
|
+
bcryptRounds?: number;
|
|
107
119
|
/** Session cookie cache settings (optional — sensible defaults applied) */
|
|
108
120
|
session?: Partial<SessionCookieConfig>;
|
|
109
121
|
/** Optional config forwarded to validation() dependency */
|
|
@@ -120,6 +132,12 @@ interface JwtPayload {
|
|
|
120
132
|
userId: string;
|
|
121
133
|
/** Unique token ID for blacklist-based revocation */
|
|
122
134
|
jti: string;
|
|
135
|
+
/**
|
|
136
|
+
* Refresh-token session/family identifier. Present on both refresh tokens
|
|
137
|
+
* (required) and access tokens (so a single family's revocation can reject
|
|
138
|
+
* every access token minted for that session, not just the presented one).
|
|
139
|
+
*/
|
|
140
|
+
tokenFamily?: string;
|
|
123
141
|
/** Per-user access token generation version for mass invalidation */
|
|
124
142
|
sessionVersion?: number;
|
|
125
143
|
/** User roles (included for client-side RBAC) */
|
|
@@ -139,13 +157,20 @@ interface TokenPair {
|
|
|
139
157
|
refreshTokenExpiresAt?: number;
|
|
140
158
|
}
|
|
141
159
|
/**
|
|
142
|
-
* User data structure for authentication
|
|
160
|
+
* User data structure for authentication.
|
|
161
|
+
*
|
|
162
|
+
* This is the minimal contract the `USER` ALS token is guaranteed to satisfy
|
|
163
|
+
* on every resolution path (Bearer token, refresh cookie, and the signed
|
|
164
|
+
* session-cookie hot path). Guards and handlers must not rely on fields
|
|
165
|
+
* outside this contract — the DB-backed paths include more, the session
|
|
166
|
+
* cookie does not.
|
|
143
167
|
*/
|
|
144
168
|
interface AuthUser {
|
|
145
169
|
id: string;
|
|
146
170
|
email: string;
|
|
147
171
|
name?: string | null;
|
|
148
172
|
role?: string;
|
|
173
|
+
status?: string;
|
|
149
174
|
permissions?: string[];
|
|
150
175
|
}
|
|
151
176
|
|
|
@@ -343,6 +368,7 @@ declare function getAuthLocale(lang: string): Record<string, any>;
|
|
|
343
368
|
declare const AUTH_SUPPORTED_LANGUAGES: string[];
|
|
344
369
|
|
|
345
370
|
declare class EncryptionService {
|
|
371
|
+
private config?;
|
|
346
372
|
private encryptionKey;
|
|
347
373
|
constructor(encryptionKey?: string | null);
|
|
348
374
|
hashPassword(password: string): Promise<string>;
|
|
@@ -352,11 +378,13 @@ declare class EncryptionService {
|
|
|
352
378
|
}
|
|
353
379
|
|
|
354
380
|
interface SessionCookieData {
|
|
381
|
+
/** Must satisfy the AuthUser contract — see AuthUser in ../types. */
|
|
355
382
|
user: {
|
|
356
383
|
id: string;
|
|
357
384
|
email: string;
|
|
358
385
|
name?: string | null;
|
|
359
386
|
role?: string;
|
|
387
|
+
status?: string;
|
|
360
388
|
};
|
|
361
389
|
roles: string[];
|
|
362
390
|
permissions: string[];
|
|
@@ -402,24 +430,25 @@ declare class UserRepository {
|
|
|
402
430
|
private get users();
|
|
403
431
|
private get roles();
|
|
404
432
|
/** Shared query helper */
|
|
433
|
+
private queryHelper?;
|
|
405
434
|
private get q();
|
|
406
|
-
getAll(): Promise<UserWithPermissions[]>;
|
|
435
|
+
getAll(limit?: number, offset?: number): Promise<UserWithPermissions[]>;
|
|
407
436
|
getById(id: string): Promise<UserWithPermissions | undefined>;
|
|
437
|
+
existsById(id: string): Promise<boolean>;
|
|
408
438
|
getRawById(id: string): Promise<User | undefined>;
|
|
409
439
|
getByEmail(email: string): Promise<(User & {
|
|
410
440
|
role?: string | null;
|
|
411
441
|
}) | undefined>;
|
|
412
442
|
create(data: NewUser): Promise<User>;
|
|
413
|
-
update(id: string, data: Partial<NewUser>): Promise<User>;
|
|
443
|
+
update(id: string, data: Partial<NewUser>): Promise<User | undefined>;
|
|
414
444
|
updateLastLogin(id: string): Promise<User>;
|
|
415
445
|
incrementFailedAttempts(id: string): Promise<User>;
|
|
416
446
|
resetFailedAttempts(id: string): Promise<User>;
|
|
417
447
|
setLockout(id: string, until: string): Promise<User>;
|
|
418
|
-
delete(id: string): Promise<User>;
|
|
448
|
+
delete(id: string): Promise<User | undefined>;
|
|
419
449
|
deleteAll(): Promise<User[]>;
|
|
420
450
|
getRoleNameById(userId: string): Promise<string | null>;
|
|
421
451
|
findByPhone(phone: string): Promise<UserWithPermissions | undefined>;
|
|
422
|
-
getUserPassword(email: string): Promise<string | undefined>;
|
|
423
452
|
getUserPermissions(userId: string): Promise<string[]>;
|
|
424
453
|
updatePhone(id: string, phone: string): Promise<User>;
|
|
425
454
|
}
|
|
@@ -441,7 +470,7 @@ declare class UserValidator {
|
|
|
441
470
|
/**
|
|
442
471
|
* Check if user exists by ID
|
|
443
472
|
*/
|
|
444
|
-
checkUserExists(id: string): Promise<
|
|
473
|
+
checkUserExists(id: string): Promise<boolean>;
|
|
445
474
|
/**
|
|
446
475
|
* Check if user exists by email
|
|
447
476
|
*/
|
|
@@ -643,11 +672,16 @@ declare class UserService {
|
|
|
643
672
|
private encryptionService;
|
|
644
673
|
private i18nService;
|
|
645
674
|
private authConfig;
|
|
675
|
+
private t;
|
|
646
676
|
constructor(roleValidator: RoleValidator, roleService: RoleService, userRepository: UserRepository, userValidator: UserValidator, encryptionService: EncryptionService, i18nService: I18nService, authConfig: AuthConfig);
|
|
647
677
|
private sanitizeUser;
|
|
648
678
|
private sanitizeUsers;
|
|
679
|
+
private requireUser;
|
|
649
680
|
private resolveUserRole;
|
|
650
|
-
getAll(
|
|
681
|
+
getAll(options?: {
|
|
682
|
+
limit?: number;
|
|
683
|
+
offset?: number;
|
|
684
|
+
}): Promise<SanitizedUser[]>;
|
|
651
685
|
getById(id: string): Promise<SanitizedUser>;
|
|
652
686
|
getByEmail(email: string): Promise<SanitizedUser>;
|
|
653
687
|
/**
|
|
@@ -663,7 +697,6 @@ declare class UserService {
|
|
|
663
697
|
delete(id: string): Promise<SanitizedUser>;
|
|
664
698
|
deleteAll(): Promise<SanitizedUser[]>;
|
|
665
699
|
getRoleName(id: string): Promise<string | null>;
|
|
666
|
-
getPassword(email: string): Promise<string | undefined>;
|
|
667
700
|
updateLastLogin(id: string): Promise<void>;
|
|
668
701
|
incrementFailedAttempts(id: string): Promise<number>;
|
|
669
702
|
resetFailedAttempts(id: string): Promise<void>;
|
|
@@ -684,9 +717,15 @@ declare class TokenRepository {
|
|
|
684
717
|
private schema;
|
|
685
718
|
private get tokens();
|
|
686
719
|
private get users();
|
|
687
|
-
private get roles();
|
|
688
720
|
/** Shared query helper */
|
|
721
|
+
private queryHelper?;
|
|
689
722
|
private get q();
|
|
723
|
+
/**
|
|
724
|
+
* Upsert the refresh-token row for a session, keyed on `tokenFamily` (the
|
|
725
|
+
* per-login session identifier, unique). A brand-new login inserts a fresh
|
|
726
|
+
* family row; a refresh rotation updates only that family's row, leaving the
|
|
727
|
+
* user's other sessions untouched.
|
|
728
|
+
*/
|
|
690
729
|
storeRefreshToken(tokenData: {
|
|
691
730
|
userId: string;
|
|
692
731
|
token: string;
|
|
@@ -696,10 +735,27 @@ declare class TokenRepository {
|
|
|
696
735
|
previousValidUntil?: string | null;
|
|
697
736
|
previousUsedAt?: string | null;
|
|
698
737
|
}): Promise<any>;
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
738
|
+
/**
|
|
739
|
+
* Claim the previous-token grace slot for a single family. Conditional on
|
|
740
|
+
* BOTH the stored previousHash still matching the presented token AND
|
|
741
|
+
* previousUsedAt being NULL. Gating on the hash (not just the flag) closes
|
|
742
|
+
* the rotation race: the winner's rotation rewrites previousHash via
|
|
743
|
+
* storeRefreshToken (and resets previousUsedAt to NULL), so a loser whose
|
|
744
|
+
* UPDATE lands after that rotation no longer matches and gets zero rows —
|
|
745
|
+
* exactly one caller ever claims the slot.
|
|
746
|
+
*/
|
|
747
|
+
markPreviousUsed(tokenFamily: string, previousHash: string): Promise<any>;
|
|
748
|
+
/** Look up a single session's token row by its family identifier. */
|
|
749
|
+
getByFamily(tokenFamily: string): Promise<any>;
|
|
750
|
+
/** Revoke a single session (one family). */
|
|
751
|
+
revokeFamily(tokenFamily: string): Promise<any>;
|
|
752
|
+
/** Revoke every session for a user (password change/reset, logout-all). */
|
|
753
|
+
revokeAllForUser(userId: string): Promise<any>;
|
|
754
|
+
/**
|
|
755
|
+
* Opportunistic cleanup: with one row per family (no unique userId), expired
|
|
756
|
+
* and abandoned sessions accumulate. Delete every expired row.
|
|
757
|
+
*/
|
|
758
|
+
deleteExpired(): Promise<any>;
|
|
703
759
|
isUserExists(userId: string): Promise<boolean>;
|
|
704
760
|
getRoleNameById(userId: string): Promise<string>;
|
|
705
761
|
getUserPermissions(userId: string): Promise<string[]>;
|
|
@@ -723,37 +779,40 @@ declare class TokenService {
|
|
|
723
779
|
private get blacklistPrefix();
|
|
724
780
|
private get resetTokenPrefix();
|
|
725
781
|
private get sessionVersionPrefix();
|
|
782
|
+
private sessionVersionKey;
|
|
783
|
+
private accessTokenTtlMs;
|
|
784
|
+
private expiresAt;
|
|
785
|
+
private getCacheValues;
|
|
786
|
+
private parseSessionVersion;
|
|
726
787
|
extractAccessToken(authorization: string): string;
|
|
727
788
|
/**
|
|
728
789
|
* Verify access token and check blacklist
|
|
729
790
|
* Throws error if token is invalid, expired, or blacklisted
|
|
730
791
|
*/
|
|
731
792
|
verifyAccessToken(token: string): Promise<JwtPayload>;
|
|
732
|
-
verifyRefreshToken(token: string):
|
|
733
|
-
private static readonly PREVIOUS_GRACE_SECONDS;
|
|
734
|
-
/**
|
|
735
|
-
* Validate a refresh token as an active session:
|
|
736
|
-
* 1. Verify JWT signature and type claim
|
|
737
|
-
* 2. Compare hash against current stored token
|
|
738
|
-
* 3. If mismatch, check previous hash within short grace window
|
|
739
|
-
* 4. Reject anything older or outside the grace window
|
|
740
|
-
*/
|
|
741
|
-
validateRefreshSession(refreshToken: string): Promise<{
|
|
793
|
+
verifyRefreshToken(token: string): {
|
|
742
794
|
userId: string;
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
};
|
|
747
|
-
}>;
|
|
795
|
+
tokenFamily: string;
|
|
796
|
+
};
|
|
797
|
+
private static readonly PREVIOUS_GRACE_SECONDS;
|
|
748
798
|
/**
|
|
749
799
|
* Read the refresh cookie and return the userId it belongs to.
|
|
750
800
|
* Validates against current/previous token state with bounded recovery.
|
|
751
801
|
* Throws if the cookie is missing, invalid, or outside the grace window.
|
|
802
|
+
*
|
|
803
|
+
* Intentionally side-effect-free: unlike refreshTokens(), a mismatch here
|
|
804
|
+
* does NOT revoke the suspect family. This is a read path (e.g. GET
|
|
805
|
+
* /auth/me) — a stray stale cookie on a read must not be able to destroy
|
|
806
|
+
* the active session. Reuse detection and revocation belong to the
|
|
807
|
+
* rotation path only.
|
|
752
808
|
*/
|
|
753
809
|
resolveUserFromCookie(): Promise<string>;
|
|
754
810
|
getUser(auth: string): Promise<any>;
|
|
811
|
+
getUserById(userId: string): Promise<any>;
|
|
755
812
|
private hashToken;
|
|
756
813
|
getTokenExpire(token: string): number | undefined;
|
|
814
|
+
decodeAccessToken(token: string): JwtPayload | null;
|
|
815
|
+
private signAccessToken;
|
|
757
816
|
/**
|
|
758
817
|
* Generate access token with unique jti for blacklist support.
|
|
759
818
|
* Includes roles/permissions for client-side RBAC/PBAC.
|
|
@@ -762,14 +821,22 @@ declare class TokenService {
|
|
|
762
821
|
userId: string;
|
|
763
822
|
roles?: string[];
|
|
764
823
|
permissions?: string[];
|
|
824
|
+
tokenFamily?: string;
|
|
765
825
|
}): Promise<string>;
|
|
766
826
|
/**
|
|
767
|
-
* Generate refresh token with unique jti
|
|
827
|
+
* Generate refresh token with unique jti. The token carries its session's
|
|
828
|
+
* family so rotation/revocation can target a single session.
|
|
768
829
|
*/
|
|
830
|
+
private signRefreshToken;
|
|
769
831
|
generateRefreshToken(data: {
|
|
770
832
|
userId: string;
|
|
833
|
+
tokenFamily?: string;
|
|
771
834
|
}): string;
|
|
772
835
|
generateTokens(userId: string, tokenFamily?: string): Promise<{
|
|
836
|
+
userId: string;
|
|
837
|
+
tokenFamily: string;
|
|
838
|
+
roles: string[];
|
|
839
|
+
permissions: string[];
|
|
773
840
|
accessToken: string;
|
|
774
841
|
refreshToken: string;
|
|
775
842
|
accessTokenExpiresAt: number;
|
|
@@ -799,17 +866,60 @@ declare class TokenService {
|
|
|
799
866
|
* Compares provided token with hashed version in database
|
|
800
867
|
*/
|
|
801
868
|
refreshTokens(): Promise<{
|
|
869
|
+
userId: string;
|
|
870
|
+
tokenFamily: string;
|
|
871
|
+
roles: string[];
|
|
872
|
+
permissions: string[];
|
|
802
873
|
accessToken: string;
|
|
803
874
|
refreshToken: string;
|
|
804
875
|
accessTokenExpiresAt: number;
|
|
805
876
|
refreshTokenExpiresAt: number;
|
|
806
877
|
}>;
|
|
807
|
-
|
|
878
|
+
/** Revoke every refresh session for a user (password change/reset, logout-all). */
|
|
879
|
+
revokeAllForUser(userId: string): Promise<any>;
|
|
880
|
+
/** Revoke a single refresh session (one family). */
|
|
881
|
+
revokeFamily(tokenFamily: string): Promise<any>;
|
|
882
|
+
/**
|
|
883
|
+
* Opportunistic cleanup of expired/abandoned sessions. With one row per
|
|
884
|
+
* family (no unique userId), abandoned logins would otherwise accumulate.
|
|
885
|
+
* Best-effort — never let cleanup failure break the calling flow.
|
|
886
|
+
*/
|
|
887
|
+
deleteExpiredSessions(): Promise<void>;
|
|
808
888
|
invalidateUserAccessTokens(userId: string): Promise<number>;
|
|
889
|
+
getUserFromCookie(): Promise<any>;
|
|
890
|
+
private get revokedFamilyPrefix();
|
|
891
|
+
private revokedFamilyKey;
|
|
892
|
+
/**
|
|
893
|
+
* Mark a family as revoked in cache for the access-token TTL, so every
|
|
894
|
+
* access token minted for that family (not just the presented one) is
|
|
895
|
+
* rejected by verifyAccessToken until it would have expired anyway.
|
|
896
|
+
*/
|
|
897
|
+
private markFamilyRevoked;
|
|
809
898
|
/**
|
|
810
|
-
*
|
|
899
|
+
* Revoke only the suspect family — NOT the whole user. Bumping the global
|
|
900
|
+
* per-user session version here would kill every device's access tokens on a
|
|
901
|
+
* single family's reuse detection. Instead drop the family's refresh row and
|
|
902
|
+
* mark the family revoked so its access tokens stop verifying.
|
|
903
|
+
*/
|
|
904
|
+
private revokeSuspectRefreshFamily;
|
|
905
|
+
/**
|
|
906
|
+
* Logout the CURRENT session only — blacklist the presented access token,
|
|
907
|
+
* mark its family revoked, and delete that family's refresh row. Other
|
|
908
|
+
* devices/sessions for the same user keep working. Use a password change or
|
|
909
|
+
* reset (revoke-all) to terminate every session.
|
|
910
|
+
*
|
|
911
|
+
* The family is resolved from, in order: a verified Bearer access token's
|
|
912
|
+
* `tokenFamily` claim, then a verified refresh cookie whose hash still
|
|
913
|
+
* matches the current family row. If neither is available, fall back to
|
|
914
|
+
* revoke-all.
|
|
811
915
|
*/
|
|
812
916
|
logout(userId: string, authorization?: string): Promise<void>;
|
|
917
|
+
/**
|
|
918
|
+
* Resolve a logout target from the refresh cookie only if the cookie maps to
|
|
919
|
+
* the user's current/valid family row. This mirrors resolveUserFromCookie()
|
|
920
|
+
* without throwing, because logout can still fall back to revoke-all.
|
|
921
|
+
*/
|
|
922
|
+
private resolveRefreshCookieFamily;
|
|
813
923
|
/**
|
|
814
924
|
* Generate secure password reset token
|
|
815
925
|
* Returns both the plain token (to send via email) and userId for identification
|
|
@@ -883,6 +993,10 @@ declare const assignRoleParams: z.ZodObject<{
|
|
|
883
993
|
userId: z.ZodString;
|
|
884
994
|
roleId: z.ZodString;
|
|
885
995
|
}, z.core.$strip>;
|
|
996
|
+
declare const userListQuery: z.ZodObject<{
|
|
997
|
+
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
998
|
+
offset: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
999
|
+
}, z.core.$strip>;
|
|
886
1000
|
type CreateUserDto = z.infer<typeof createUserDto>;
|
|
887
1001
|
type UpdateUserDto = z.infer<typeof updateUserDto>;
|
|
888
1002
|
type UserIdParam = z.infer<typeof userIdParam>;
|
|
@@ -894,26 +1008,25 @@ type LanguageParam = z.infer<typeof languageParam>;
|
|
|
894
1008
|
type EmailParam = z.infer<typeof emailParam>;
|
|
895
1009
|
type UserIdInParam = z.infer<typeof userIdInParam>;
|
|
896
1010
|
type AssignRoleParams = z.infer<typeof assignRoleParams>;
|
|
1011
|
+
type UserListQuery = z.infer<typeof userListQuery>;
|
|
897
1012
|
|
|
898
1013
|
declare class AuthService {
|
|
899
1014
|
private tokenService;
|
|
900
1015
|
private userService;
|
|
901
1016
|
private userValidator;
|
|
1017
|
+
private encryptionService;
|
|
902
1018
|
private cookieManager;
|
|
903
1019
|
private i18nService;
|
|
904
1020
|
private emailService;
|
|
905
1021
|
private config;
|
|
906
1022
|
private t;
|
|
907
1023
|
private logger;
|
|
908
|
-
private
|
|
909
|
-
constructor(tokenService: TokenService, userService: UserService, userValidator: UserValidator, cookieManager: CookieManager, i18nService: I18nService, emailService: EmailService);
|
|
1024
|
+
private dummyHash?;
|
|
1025
|
+
constructor(tokenService: TokenService, userService: UserService, userValidator: UserValidator, encryptionService: EncryptionService, cookieManager: CookieManager, i18nService: I18nService, emailService: EmailService);
|
|
910
1026
|
private isLockoutActive;
|
|
911
1027
|
private nextLockoutUntil;
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
* Used to extract accurate roles/permissions for the session cookie cache.
|
|
915
|
-
*/
|
|
916
|
-
private decodeAccessToken;
|
|
1028
|
+
private getDummyHash;
|
|
1029
|
+
warmupPasswordHash(): Promise<void>;
|
|
917
1030
|
registerUser(body: CreateUserDto): Promise<SanitizedUser>;
|
|
918
1031
|
loginUser(body: LoginDto): Promise<TokenPair & {
|
|
919
1032
|
user: SanitizedUser;
|
|
@@ -923,6 +1036,13 @@ declare class AuthService {
|
|
|
923
1036
|
data: any;
|
|
924
1037
|
message: string;
|
|
925
1038
|
}>;
|
|
1039
|
+
/**
|
|
1040
|
+
* Prune expired refresh sessions for every user. Login already prunes
|
|
1041
|
+
* opportunistically; expose this so consumers can also run it from a
|
|
1042
|
+
* scheduled job (cron / queue) to reclaim rows from users who never return.
|
|
1043
|
+
* Best-effort — safe to call repeatedly.
|
|
1044
|
+
*/
|
|
1045
|
+
pruneExpiredSessions(): Promise<void>;
|
|
926
1046
|
getUserProfile(userData: AuthUser): Promise<AuthUser & {
|
|
927
1047
|
language: string;
|
|
928
1048
|
}>;
|
|
@@ -934,10 +1054,7 @@ declare class AuthService {
|
|
|
934
1054
|
* fall back to cookie when no Authorization header is present.
|
|
935
1055
|
*
|
|
936
1056
|
* Refreshes the session cookie cache only when authoritative roles/permissions
|
|
937
|
-
* are available
|
|
938
|
-
* fallback, we cannot reliably populate permissions without a DB round-trip,
|
|
939
|
-
* so we leave the existing cache alone — it will be refreshed by the next
|
|
940
|
-
* login/refresh cycle.
|
|
1057
|
+
* are available from the access token.
|
|
941
1058
|
*/
|
|
942
1059
|
getMe(authorization?: string): Promise<SanitizedUser & {
|
|
943
1060
|
language: string;
|
|
@@ -953,7 +1070,6 @@ declare class AuthService {
|
|
|
953
1070
|
}>;
|
|
954
1071
|
}
|
|
955
1072
|
|
|
956
|
-
declare const setConfiguredCookieName: (name: string) => void;
|
|
957
1073
|
declare class AuthController {
|
|
958
1074
|
private authService;
|
|
959
1075
|
constructor(authService: AuthService);
|
|
@@ -1013,6 +1129,11 @@ declare class AuthResolver {
|
|
|
1013
1129
|
role?: string;
|
|
1014
1130
|
permissions?: string[];
|
|
1015
1131
|
} | false>;
|
|
1132
|
+
resolveFromSessionCookie(): Promise<{
|
|
1133
|
+
user: any;
|
|
1134
|
+
role?: string;
|
|
1135
|
+
permissions?: string[];
|
|
1136
|
+
} | false>;
|
|
1016
1137
|
/**
|
|
1017
1138
|
* Resolve the current user from the refresh cookie (cookie-only flow,
|
|
1018
1139
|
* e.g. Next.js Server Components calling /auth/me with just the cookie).
|
|
@@ -1027,6 +1148,7 @@ declare class AuthResolver {
|
|
|
1027
1148
|
permissions?: string[];
|
|
1028
1149
|
} | false>;
|
|
1029
1150
|
activate(): Promise<void>;
|
|
1151
|
+
onReady(): Promise<void>;
|
|
1030
1152
|
}
|
|
1031
1153
|
|
|
1032
1154
|
interface RunAsUser {
|
|
@@ -1691,6 +1813,7 @@ declare class AuthQueries {
|
|
|
1691
1813
|
createdAt: any;
|
|
1692
1814
|
updatedAt: any;
|
|
1693
1815
|
};
|
|
1816
|
+
getUserWithPermissions(where: any): Promise<any | undefined>;
|
|
1694
1817
|
/**
|
|
1695
1818
|
* Get permissions for a user by their userId
|
|
1696
1819
|
*/
|
|
@@ -1769,7 +1892,7 @@ type RevokeTokenDto = z.infer<typeof revokeTokenDto>;
|
|
|
1769
1892
|
declare class UserController {
|
|
1770
1893
|
private userService;
|
|
1771
1894
|
constructor(userService: UserService);
|
|
1772
|
-
getUsers(): Promise<SanitizedUser[]>;
|
|
1895
|
+
getUsers(query: UserListQuery): Promise<SanitizedUser[]>;
|
|
1773
1896
|
getLang(): Promise<{
|
|
1774
1897
|
language: string;
|
|
1775
1898
|
}>;
|
|
@@ -1808,6 +1931,8 @@ interface SeedUserConfig {
|
|
|
1808
1931
|
interface AuthSeedConfig {
|
|
1809
1932
|
adminEmail: string;
|
|
1810
1933
|
adminPass: string;
|
|
1934
|
+
/** Bcrypt work factor (default: 10) */
|
|
1935
|
+
bcryptRounds?: number;
|
|
1811
1936
|
roles?: Array<{
|
|
1812
1937
|
name: string;
|
|
1813
1938
|
description?: string;
|
|
@@ -1830,6 +1955,8 @@ interface SeedAuthDataConfig {
|
|
|
1830
1955
|
adminEmail: string;
|
|
1831
1956
|
/** Admin password (required) */
|
|
1832
1957
|
adminPassword: string;
|
|
1958
|
+
/** Bcrypt work factor (default: 10) */
|
|
1959
|
+
bcryptRounds?: number;
|
|
1833
1960
|
/** Additional users to seed (optional) */
|
|
1834
1961
|
users?: SeedUserConfig[];
|
|
1835
1962
|
/** Custom roles (optional - uses defaults if omitted) */
|
|
@@ -1922,4 +2049,4 @@ declare const authSeed: (config: AuthSeedConfig) => Record<string, SeedEntry>;
|
|
|
1922
2049
|
*/
|
|
1923
2050
|
declare function seedAuthData(config: SeedAuthDataConfig): Promise<SeedAuthDataResult>;
|
|
1924
2051
|
|
|
1925
|
-
export { AUTH_CONFIG, en as AUTH_EN, AUTH_LOCALES, AUTH_MODULE, AUTH_PERMISSIONS, AUTH_ROLE, AUTH_SCHEMA, AUTH_SUPPORTED_LANGUAGES, AUTH_USER, type AssignPermissionDto, type AssignRoleDto, type AssignRoleParams, type AuthConfig, AuthController, AuthGuard, type AuthPluginConfig, AuthQueries, AuthResolver, type AuthSchema, type AuthSeedConfig, AuthService, type AuthUser, Can, CanCreate, CanDelete, CanList, CanRead, CanUpdate, type ChainableGuard, type ChangePasswordDto, type CheckPermissionDto, type ConfiguredOwnership, type ConfirmResetPasswordDto, CookieManager, type CreatePermissionDto, type CreateRoleDto, type CreateTokenDto, type CreateUserDto, type DefineRolesOptions, type EmailParam, EncryptionService, type JwtConfig, type JwtPayload, type LanguageParam, type LoginDto, NewPermission, NewRoleEntity, NewUser, Owned, type OwnedMethods, type OwnershipConfig, type OwnershipProvider, type OwnershipRule, OwnershipToken, type OwnershipTokenOptions, Permission, PermissionController, PermissionGuard, type PermissionIdParam, PermissionRepository, PermissionService, PermissionValidator, Policy, ROLES, ROLE_GROUPS, type RefreshTokenDto, type ResetPasswordDto, type ResourceAccessor, type ResourceGuards, type ResourceGuardsOptions, type RevokeTokenDto, Role, RoleController, RoleEntity, RoleGuard, type RoleIdParam, type RoleInput, RolePermission, RoleRepository, RoleService, type RoleType, RoleValidator, type RunAsUser, type SanitizedUser, ScopeContext, type ScopeResult, type SeedAuthDataConfig, type SeedAuthDataResult, type SeedUserConfig, type SessionCookieData, TOKEN_STATUS, TOKEN_TYPE, type TokenIdParam, type TokenPair, TokenRepository, TokenService, USER_STATUS, type UpdatePermissionDto, type UpdateRoleDto, type UpdateTokenDto, type UpdateUserDto, User, UserController, type UserIdInParam, type UserIdParam, UserRepository, UserService, UserValidator, type UserWithPermissions, type VerifyTokenDto, assignPermissionDto, assignRoleDto, assignRoleParams, auth$1 as auth, authSeed, avatarsPath, calculateAge, calculateYearsOfExperience, changePasswordDto, checkPermissionDto, clean, configureOwnership, confirmResetPasswordDto, createPermissionDto, createRoleDto, createTokenDto, createUserDto, defineRoles, emailParam, formatDate, getAuthLocale, getAvatarFile, isAdmin, isAdministrator, isAuth, isEmpty, isFile, isPath, join, languageParam, loginDto, own, parseSchema, permissionIdParam, pickProps, refreshTokenDto, resetPasswordDto, revokeTokenDto, roleIdParam, runAsUser, seedAuthData,
|
|
2052
|
+
export { AUTH_CONFIG, en as AUTH_EN, AUTH_LOCALES, AUTH_MODULE, AUTH_PERMISSIONS, AUTH_ROLE, AUTH_SCHEMA, AUTH_SUPPORTED_LANGUAGES, AUTH_USER, type AssignPermissionDto, type AssignRoleDto, type AssignRoleParams, type AuthConfig, AuthController, AuthGuard, type AuthPluginConfig, AuthQueries, AuthResolver, type AuthSchema, type AuthSeedConfig, AuthService, type AuthUser, Can, CanCreate, CanDelete, CanList, CanRead, CanUpdate, type ChainableGuard, type ChangePasswordDto, type CheckPermissionDto, type ConfiguredOwnership, type ConfirmResetPasswordDto, CookieManager, type CreatePermissionDto, type CreateRoleDto, type CreateTokenDto, type CreateUserDto, type DefineRolesOptions, type EmailParam, EncryptionService, type JwtConfig, type JwtPayload, type LanguageParam, type LoginDto, NewPermission, NewRoleEntity, NewUser, Owned, type OwnedMethods, type OwnershipConfig, type OwnershipProvider, type OwnershipRule, OwnershipToken, type OwnershipTokenOptions, Permission, PermissionController, PermissionGuard, type PermissionIdParam, PermissionRepository, PermissionService, PermissionValidator, Policy, ROLES, ROLE_GROUPS, type RefreshTokenDto, type ResetPasswordDto, type ResourceAccessor, type ResourceGuards, type ResourceGuardsOptions, type RevokeTokenDto, Role, RoleController, RoleEntity, RoleGuard, type RoleIdParam, type RoleInput, RolePermission, RoleRepository, RoleService, type RoleType, RoleValidator, type RunAsUser, type SanitizedUser, ScopeContext, type ScopeResult, type SeedAuthDataConfig, type SeedAuthDataResult, type SeedUserConfig, type SessionCookieData, TOKEN_STATUS, TOKEN_TYPE, type TokenIdParam, type TokenPair, TokenRepository, TokenService, USER_STATUS, type UpdatePermissionDto, type UpdateRoleDto, type UpdateTokenDto, type UpdateUserDto, User, UserController, type UserIdInParam, type UserIdParam, type UserListQuery, UserRepository, UserService, UserValidator, type UserWithPermissions, type VerifyTokenDto, assignPermissionDto, assignRoleDto, assignRoleParams, auth$1 as auth, authSeed, avatarsPath, calculateAge, calculateYearsOfExperience, changePasswordDto, checkPermissionDto, clean, configureOwnership, confirmResetPasswordDto, createPermissionDto, createRoleDto, createTokenDto, createUserDto, defineRoles, emailParam, formatDate, getAuthLocale, getAvatarFile, isAdmin, isAdministrator, isAuth, isEmpty, isFile, isPath, join, languageParam, loginDto, own, parseSchema, permissionIdParam, pickProps, refreshTokenDto, resetPasswordDto, revokeTokenDto, roleIdParam, runAsUser, seedAuthData, tokenIdParam, updatePermissionDto, updateRoleDto, updateTokenDto, updateUserDto, userIdInParam, userIdParam, userListQuery, verifyTokenDto, where };
|