najm-auth 3.1.4 → 3.1.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 CHANGED
@@ -272,9 +272,13 @@ login, `AuthSessionService.establish()`, Google OAuth (which redirects with
272
272
  recovery — so verified-email OAuth linking cannot skip it. Marking a new
273
273
  requirement also revokes the user's current sessions.
274
274
 
275
- `withAuthCookiePersistence` recognizes the setup response on its own: it drops
276
- any session cookies the response carried, clears the remembered preference, and
277
- leaves the opaque setup cookie alone.
275
+ `withAuthCookiePersistence` recognizes logout and setup boundaries on its own.
276
+ After a successful logout it drops stale auth-cookie issuances and guarantees
277
+ exactly one deletion for each configured auth cookie. It preserves a valid
278
+ upstream deletion (including a custom cookie path), or synthesizes a canonical
279
+ deletion when one is missing. A setup response gets the same auth-cookie
280
+ deletions, clears the remembered preference, and leaves the opaque setup cookie
281
+ alone.
278
282
 
279
283
  ### Google Sign-In
280
284
 
@@ -721,7 +725,7 @@ limits are active when `auth()` is registered.
721
725
  | Route | Limit | Window | Key Strategy |
722
726
  |-------|-------|--------|--------------|
723
727
  | `POST /auth/register` | 5 | 15 minutes | IP |
724
- | `POST /auth/login` | 8 | 10 minutes | IP + hashed normalized identity |
728
+ | `POST /auth/login` | 8 | 10 minutes | IP + hashed normalized identity |
725
729
  | `POST /auth/refresh` | 15 | 15 minutes | Cookie fingerprint |
726
730
  | `POST /auth/session/recover` | 120 | 1 minute | Cookie fingerprint |
727
731
  | `POST /auth/logout` | 10 | 15 minutes | User ID |
@@ -729,27 +733,27 @@ limits are active when `auth()` is registered.
729
733
  | `POST /auth/forgot-password` | 3 | 15 minutes | IP |
730
734
  | `POST /auth/reset-password` | 5 | 15 minutes | IP |
731
735
 
732
- ### Customizing Rate Limits
733
-
734
- The login route has strict environment overrides. Values are read when the
735
- server imports `najm-auth`, so restart the process after changing them. Invalid
736
- values fail startup rather than silently weakening the limiter.
737
-
738
- ```bash
739
- NAJM_AUTH_LOGIN_RATE_LIMIT_ENABLED=true
740
- NAJM_AUTH_LOGIN_RATE_LIMIT=8
741
- NAJM_AUTH_LOGIN_RATE_WINDOW=10m
742
- ```
743
-
744
- `NAJM_AUTH_LOGIN_RATE_LIMIT_ENABLED=false` disables only the login-route
745
- limiter. Keep it enabled on public production deployments; a shorter window is
746
- the safer setting for a disposable production-built demo.
747
-
748
- The generic plugin configuration remains available for global limits and skip
749
- rules:
750
-
751
- ```typescript
752
- auth({
736
+ ### Customizing Rate Limits
737
+
738
+ The login route has strict environment overrides. Values are read when the
739
+ server imports `najm-auth`, so restart the process after changing them. Invalid
740
+ values fail startup rather than silently weakening the limiter.
741
+
742
+ ```bash
743
+ NAJM_AUTH_LOGIN_RATE_LIMIT_ENABLED=true
744
+ NAJM_AUTH_LOGIN_RATE_LIMIT=8
745
+ NAJM_AUTH_LOGIN_RATE_WINDOW=10m
746
+ ```
747
+
748
+ `NAJM_AUTH_LOGIN_RATE_LIMIT_ENABLED=false` disables only the login-route
749
+ limiter. Keep it enabled on public production deployments; a shorter window is
750
+ the safer setting for a disposable production-built demo.
751
+
752
+ The generic plugin configuration remains available for global limits and skip
753
+ rules:
754
+
755
+ ```typescript
756
+ auth({
753
757
  rateLimit: {
754
758
  keyGenerator: 'ip', // or 'user', 'api-key', 'user+ip'
755
759
  defaultWindow: '10m',
@@ -248,8 +248,8 @@ interface AuthCookiePersistenceOptions {
248
248
  * Najm's own setup response is recognized without this — supply it only to
249
249
  * cover an application-specific shape. Such a response may carry auth
250
250
  * cookies anyway, and persisting them would leave a half-authenticated
251
- * browser that skips the setup step on reload. Returning `true` strips them
252
- * and clears the stored choice.
251
+ * browser that skips the setup step on reload. Returning `true` replaces
252
+ * them with deletions and clears the stored choice.
253
253
  */
254
254
  isSetupResponse?: (payload: unknown) => boolean;
255
255
  }
@@ -1661,6 +1661,18 @@ function clearedRememberCookie(name, secure) {
1661
1661
  ].join("; ");
1662
1662
  }
1663
1663
  __name(clearedRememberCookie, "clearedRememberCookie");
1664
+ function clearedAuthCookie(name, secure) {
1665
+ return [
1666
+ `${name}=`,
1667
+ "Path=/",
1668
+ "HttpOnly",
1669
+ "SameSite=Lax",
1670
+ ...secure ? ["Secure"] : [],
1671
+ "Expires=Thu, 01 Jan 1970 00:00:00 GMT",
1672
+ "Max-Age=0"
1673
+ ].join("; ");
1674
+ }
1675
+ __name(clearedAuthCookie, "clearedAuthCookie");
1664
1676
  function withAuthCookiePersistence(handler, options = {}) {
1665
1677
  const {
1666
1678
  authCookieNames = DEFAULTS.authCookieNames,
@@ -1697,8 +1709,15 @@ function withAuthCookiePersistence(handler, options = {}) {
1697
1709
  const headers = new Headers(response.headers);
1698
1710
  const setCookies = headers.getSetCookie();
1699
1711
  headers.delete("set-cookie");
1712
+ const clearedAuthCookies = /* @__PURE__ */ new Set();
1700
1713
  for (const setCookie of setCookies) {
1701
- if (action.type === "setup" && authCookieNames.includes(cookieName(setCookie)) && !isDeletionCookie(setCookie)) {
1714
+ const name = cookieName(setCookie);
1715
+ const isAuthCookie = authCookieNames.includes(name);
1716
+ if ((action.type === "clear" || action.type === "setup") && isAuthCookie) {
1717
+ if (isDeletionCookie(setCookie) && !clearedAuthCookies.has(name)) {
1718
+ headers.append("set-cookie", setCookie);
1719
+ clearedAuthCookies.add(name);
1720
+ }
1702
1721
  continue;
1703
1722
  }
1704
1723
  headers.append(
@@ -1706,6 +1725,13 @@ function withAuthCookiePersistence(handler, options = {}) {
1706
1725
  action.type === "apply" && action.mode === "session" ? makeSessionCookie(setCookie, authCookieNames) : setCookie
1707
1726
  );
1708
1727
  }
1728
+ if (action.type === "clear" || action.type === "setup") {
1729
+ for (const name of authCookieNames) {
1730
+ if (!clearedAuthCookies.has(name)) {
1731
+ headers.append("set-cookie", clearedAuthCookie(name, secure));
1732
+ }
1733
+ }
1734
+ }
1709
1735
  headers.append(
1710
1736
  "set-cookie",
1711
1737
  action.type === "clear" || action.type === "setup" ? clearedRememberCookie(rememberCookieName, secure) : rememberCookie(rememberCookieName, action.mode, secure, maxAgeSeconds)
package/dist/index.d.ts CHANGED
@@ -705,7 +705,6 @@ declare class UserValidator {
705
705
  * Check if user exists by email
706
706
  */
707
707
  checkUserExistsByEmail(email: string): Promise<{
708
- password: string;
709
708
  id: string;
710
709
  name: string;
711
710
  createdAt: string;
@@ -714,8 +713,9 @@ declare class UserValidator {
714
713
  emailVerified: boolean;
715
714
  phone: string;
716
715
  phoneVerified: boolean;
716
+ password: string;
717
717
  image: string;
718
- status: "active" | "pending" | "inactive";
718
+ status: "active" | "inactive" | "pending";
719
719
  roleId: string;
720
720
  lastLogin: string;
721
721
  failedLoginAttempts: number;
@@ -727,7 +727,6 @@ declare class UserValidator {
727
727
  * Check if email exists in database
728
728
  */
729
729
  checkEmailExists(email: string): Promise<{
730
- password: string;
731
730
  id: string;
732
731
  name: string;
733
732
  createdAt: string;
@@ -736,8 +735,9 @@ declare class UserValidator {
736
735
  emailVerified: boolean;
737
736
  phone: string;
738
737
  phoneVerified: boolean;
738
+ password: string;
739
739
  image: string;
740
- status: "active" | "pending" | "inactive";
740
+ status: "active" | "inactive" | "pending";
741
741
  roleId: string;
742
742
  lastLogin: string;
743
743
  failedLoginAttempts: number;
@@ -1260,8 +1260,8 @@ declare const createUserDto: z.ZodObject<{
1260
1260
  emailVerified: z.ZodDefault<z.ZodBoolean>;
1261
1261
  status: z.ZodOptional<z.ZodEnum<{
1262
1262
  active: "active";
1263
- pending: "pending";
1264
1263
  inactive: "inactive";
1264
+ pending: "pending";
1265
1265
  }>>;
1266
1266
  }, z.core.$strip>;
1267
1267
  declare const updateUserDto: z.ZodObject<{
@@ -1273,8 +1273,8 @@ declare const updateUserDto: z.ZodObject<{
1273
1273
  emailVerified: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
1274
1274
  status: z.ZodOptional<z.ZodOptional<z.ZodEnum<{
1275
1275
  active: "active";
1276
- pending: "pending";
1277
1276
  inactive: "inactive";
1277
+ pending: "pending";
1278
1278
  }>>>;
1279
1279
  }, z.core.$strip>;
1280
1280
  declare const registerDto: z.ZodObject<{
@@ -1655,7 +1655,6 @@ declare class AuthController {
1655
1655
  registerUser(body: RegisterDto): Promise<SanitizedUser>;
1656
1656
  loginUser(body: LoginDto): Promise<LoginResult>;
1657
1657
  inviteUser(body: InviteUserDto): Promise<Omit<{
1658
- password: string;
1659
1658
  id: string;
1660
1659
  name: string;
1661
1660
  createdAt: string;
@@ -1664,8 +1663,9 @@ declare class AuthController {
1664
1663
  emailVerified: boolean;
1665
1664
  phone: string;
1666
1665
  phoneVerified: boolean;
1666
+ password: string;
1667
1667
  image: string;
1668
- status: "active" | "pending" | "inactive";
1668
+ status: "active" | "inactive" | "pending";
1669
1669
  roleId: string;
1670
1670
  lastLogin: string;
1671
1671
  failedLoginAttempts: number;
@@ -1688,7 +1688,6 @@ declare class AuthController {
1688
1688
  message: string;
1689
1689
  }>;
1690
1690
  userProfile(authorization?: string): Promise<Omit<{
1691
- password: string;
1692
1691
  id: string;
1693
1692
  name: string;
1694
1693
  createdAt: string;
@@ -1697,8 +1696,9 @@ declare class AuthController {
1697
1696
  emailVerified: boolean;
1698
1697
  phone: string;
1699
1698
  phoneVerified: boolean;
1699
+ password: string;
1700
1700
  image: string;
1701
- status: "active" | "pending" | "inactive";
1701
+ status: "active" | "inactive" | "pending";
1702
1702
  roleId: string;
1703
1703
  lastLogin: string;
1704
1704
  failedLoginAttempts: number;
@@ -235,7 +235,7 @@ declare const usersTable: drizzle_orm_pg_core.PgTableWithColumns<{
235
235
  tableName: "users";
236
236
  dataType: "string";
237
237
  columnType: "PgEnumColumn";
238
- data: "active" | "pending" | "inactive";
238
+ data: "active" | "inactive" | "pending";
239
239
  driverParam: string;
240
240
  notNull: false;
241
241
  hasDefault: true;
@@ -1308,7 +1308,7 @@ declare const authSchema: {
1308
1308
  tableName: "users";
1309
1309
  dataType: "string";
1310
1310
  columnType: "PgEnumColumn";
1311
- data: "active" | "pending" | "inactive";
1311
+ data: "active" | "inactive" | "pending";
1312
1312
  driverParam: string;
1313
1313
  notNull: false;
1314
1314
  hasDefault: true;
@@ -252,7 +252,7 @@ declare const usersTable: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
252
252
  tableName: "users";
253
253
  dataType: "string";
254
254
  columnType: "SQLiteText";
255
- data: "active" | "pending" | "inactive";
255
+ data: "active" | "inactive" | "pending";
256
256
  driverParam: string;
257
257
  notNull: false;
258
258
  hasDefault: true;
@@ -265,7 +265,7 @@ declare const usersTable: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
265
265
  generated: undefined;
266
266
  }, {}, {
267
267
  length: number;
268
- $type: "active" | "pending" | "inactive";
268
+ $type: "active" | "inactive" | "pending";
269
269
  }>;
270
270
  roleId: drizzle_orm_sqlite_core.SQLiteColumn<{
271
271
  name: "role_id";
@@ -1476,7 +1476,7 @@ declare const authSchema: {
1476
1476
  tableName: "users";
1477
1477
  dataType: "string";
1478
1478
  columnType: "SQLiteText";
1479
- data: "active" | "pending" | "inactive";
1479
+ data: "active" | "inactive" | "pending";
1480
1480
  driverParam: string;
1481
1481
  notNull: false;
1482
1482
  hasDefault: true;
@@ -1489,7 +1489,7 @@ declare const authSchema: {
1489
1489
  generated: undefined;
1490
1490
  }, {}, {
1491
1491
  length: number;
1492
- $type: "active" | "pending" | "inactive";
1492
+ $type: "active" | "inactive" | "pending";
1493
1493
  }>;
1494
1494
  roleId: drizzle_orm_sqlite_core.SQLiteColumn<{
1495
1495
  name: "role_id";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "najm-auth",
3
- "version": "3.1.4",
3
+ "version": "3.1.5",
4
4
  "description": "Authentication and authorization library for najm framework",
5
5
  "type": "module",
6
6
  "files": [