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 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, MySQL)
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' | 'mysql' // Default: 'pg'
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 all users |
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
- | `GET /auth/refresh` | 10 | 15 minutes | IP |
514
- | `GET /auth/logout` | 10 | 15 minutes | User ID |
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
- - Single refresh token per user (upsert on login)
596
- - Previous sessions invalidated on new login
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 (suitable for single-instance servers)
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', 'najm-auth/sqlite', or 'najm-auth/mysql'
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
- /** Database dialect (default: 'pg'). Auto-selects the correct schema. */
88
- dialect?: 'pg' | 'sqlite' | 'mysql';
89
- /** Database schema tables (optional, overrides dialect). Use authSchema from 'najm-auth/sqlite' or 'najm-auth/mysql' */
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<UserWithPermissions>;
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(): Promise<SanitizedUser[]>;
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
- markPreviousUsed(userId: string): Promise<any>;
700
- getRefreshTokenWithFamily(userId: string): Promise<any>;
701
- revokeToken(userId: string): Promise<any>;
702
- revokeByFamily(tokenFamily: string): Promise<any>;
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): 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
- rotatedTokens?: {
744
- refreshToken: string;
745
- tokenFamily: string;
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
- revokeToken(userId: string): Promise<any>;
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
- * Logout user - blacklist access token and revoke refresh token
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 static readonly DUMMY_HASH;
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
- * Decode JWT payload without verification (token was just generated by us).
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 (from the access token JWT). When called via the refresh-cookie
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, setConfiguredCookieName, tokenIdParam, updatePermissionDto, updateRoleDto, updateTokenDto, updateUserDto, userIdInParam, userIdParam, verifyTokenDto, where };
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 };