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 +29 -25
- package/dist/client/server/index.d.ts +2 -2
- package/dist/client/server/index.js +27 -1
- package/dist/index.d.ts +10 -10
- package/dist/schema/pg.d.ts +2 -2
- package/dist/schema/sqlite.d.ts +4 -4
- package/package.json +1 -1
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
|
|
276
|
-
|
|
277
|
-
|
|
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`
|
|
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
|
-
|
|
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" | "
|
|
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" | "
|
|
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" | "
|
|
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" | "
|
|
1701
|
+
status: "active" | "inactive" | "pending";
|
|
1702
1702
|
roleId: string;
|
|
1703
1703
|
lastLogin: string;
|
|
1704
1704
|
failedLoginAttempts: number;
|
package/dist/schema/pg.d.ts
CHANGED
|
@@ -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" | "
|
|
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" | "
|
|
1311
|
+
data: "active" | "inactive" | "pending";
|
|
1312
1312
|
driverParam: string;
|
|
1313
1313
|
notNull: false;
|
|
1314
1314
|
hasDefault: true;
|
package/dist/schema/sqlite.d.ts
CHANGED
|
@@ -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" | "
|
|
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" | "
|
|
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" | "
|
|
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" | "
|
|
1492
|
+
$type: "active" | "inactive" | "pending";
|
|
1493
1493
|
}>;
|
|
1494
1494
|
roleId: drizzle_orm_sqlite_core.SQLiteColumn<{
|
|
1495
1495
|
name: "role_id";
|