kroxt 1.3.10 → 1.3.11
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 +270 -231
- package/dist/auth/adapters/drizzle.cjs +5 -0
- package/dist/auth/adapters/drizzle.cjs.map +2 -2
- package/dist/auth/adapters/drizzle.d.ts.map +1 -1
- package/dist/auth/adapters/drizzle.js +5 -0
- package/dist/auth/adapters/drizzle.js.map +2 -2
- package/dist/auth/adapters/index.cjs.map +1 -1
- package/dist/auth/adapters/index.d.ts +2 -0
- package/dist/auth/adapters/index.d.ts.map +1 -1
- package/dist/auth/adapters/memory.cjs +9 -0
- package/dist/auth/adapters/memory.cjs.map +2 -2
- package/dist/auth/adapters/memory.d.ts.map +1 -1
- package/dist/auth/adapters/memory.js +9 -0
- package/dist/auth/adapters/memory.js.map +2 -2
- package/dist/auth/adapters/mongoose.cjs +5 -0
- package/dist/auth/adapters/mongoose.cjs.map +2 -2
- package/dist/auth/adapters/mongoose.d.ts.map +1 -1
- package/dist/auth/adapters/mongoose.js +5 -0
- package/dist/auth/adapters/mongoose.js.map +2 -2
- package/dist/auth/adapters/prisma.cjs +8 -0
- package/dist/auth/adapters/prisma.cjs.map +2 -2
- package/dist/auth/adapters/prisma.d.ts.map +1 -1
- package/dist/auth/adapters/prisma.js +8 -0
- package/dist/auth/adapters/prisma.js.map +2 -2
- package/dist/auth/core/index.cjs +32 -5
- package/dist/auth/core/index.cjs.map +2 -2
- package/dist/auth/core/index.d.ts +3 -0
- package/dist/auth/core/index.d.ts.map +1 -1
- package/dist/auth/core/index.js +32 -5
- package/dist/auth/core/index.js.map +2 -2
- package/dist/cli/index.cjs +49 -18
- package/dist/cli/index.cjs.map +3 -3
- package/dist/cli/index.js +49 -18
- package/dist/cli/index.js.map +2 -2
- package/dist/cli/templates.cjs +5 -1
- package/dist/cli/templates.cjs.map +2 -2
- package/dist/cli/templates.d.ts +1 -1
- package/dist/cli/templates.d.ts.map +1 -1
- package/dist/cli/templates.js +5 -1
- package/dist/cli/templates.js.map +2 -2
- package/package.json +107 -107
|
@@ -73,6 +73,15 @@ function createMemoryAdapter() {
|
|
|
73
73
|
const record = rateLimits.get(key);
|
|
74
74
|
if (!record || now > record.resetTime) return null;
|
|
75
75
|
return record;
|
|
76
|
+
},
|
|
77
|
+
invalidateSession: async (userId) => {
|
|
78
|
+
for (const [email, user] of users.entries()) {
|
|
79
|
+
if (user.id === userId) {
|
|
80
|
+
const updatedUser = { ...user, sessionVersion: (user.sessionVersion || 0) + 1 };
|
|
81
|
+
users.set(email, updatedUser);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
76
85
|
}
|
|
77
86
|
};
|
|
78
87
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/auth/adapters/memory.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates an in-memory database adapter for the auth engine.\r\n * This is useful for testing, prototyping, or when you don't need persistent storage.\r\n * All data is kept in memory and is lost when the server restarts.\r\n */\r\nexport function createMemoryAdapter<TUser extends User = User>(): AuthAdapter<TUser> {\r\n const users = new Map<string, TUser>();\r\n const accounts = new Map<string, { userId: string; provider: string; providerId: string }>();\r\n const rateLimits = new Map<string, { count: number; resetTime: number }>();\r\n\r\n return {\r\n createUser: async (data: any) => {\r\n // Auto-generate ID if not provided\r\n const id = data.id || Date.now().toString();\r\n const newUser = { ...data, id } as TUser;\r\n\r\n // Store using email as the primary lookup key\r\n users.set(newUser.email, newUser);\r\n return newUser;\r\n },\r\n\r\n findUserByEmail: async (email: string) => {\r\n return users.get(email) || null;\r\n },\r\n\r\n findUserById: async (id: string) => {\r\n for (const user of users.values()) {\r\n if (user.id === id) {\r\n return user;\r\n }\r\n }\r\n return null;\r\n },\r\n\r\n updateUser: async (id: string, data: Partial<TUser>) => {\r\n for (const [email, user] of users.entries()) {\r\n if (user.id === id) {\r\n const updatedUser = { ...user, ...data } as TUser;\r\n users.set(email, updatedUser);\r\n return updatedUser;\r\n }\r\n }\r\n return null;\r\n },\r\n\r\n linkOAuthAccount: async (userId: string, provider: string, providerId: string) => {\r\n const accountId = `${provider}_${providerId}`;\r\n accounts.set(accountId, { userId, provider, providerId });\r\n },\r\n\r\n incrementRateLimit: async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n const record = rateLimits.get(key);\r\n\r\n if (!record || now > record.resetTime) {\r\n const newRecord = { count: 1, resetTime: now + windowMs };\r\n rateLimits.set(key, newRecord);\r\n return newRecord;\r\n }\r\n\r\n record.count += 1;\r\n return record;\r\n },\r\n\r\n getRateLimit: async (key: string) => {\r\n const now = Date.now();\r\n const record = rateLimits.get(key);\r\n if (!record || now > record.resetTime) return null;\r\n return record;\r\n }\r\n };\r\n}\r\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOO,SAAS,sBAAqE;AACjF,QAAM,QAAQ,oBAAI,IAAmB;AACrC,QAAM,WAAW,oBAAI,IAAsE;AAC3F,QAAM,aAAa,oBAAI,IAAkD;AAEzE,SAAO;AAAA,IACH,YAAY,OAAO,SAAc;AAE7B,YAAM,KAAK,KAAK,MAAM,KAAK,IAAI,EAAE,SAAS;AAC1C,YAAM,UAAU,EAAE,GAAG,MAAM,GAAG;AAG9B,YAAM,IAAI,QAAQ,OAAO,OAAO;AAChC,aAAO;AAAA,IACX;AAAA,IAEA,iBAAiB,OAAO,UAAkB;AACtC,aAAO,MAAM,IAAI,KAAK,KAAK;AAAA,IAC/B;AAAA,IAEA,cAAc,OAAO,OAAe;AAChC,iBAAW,QAAQ,MAAM,OAAO,GAAG;AAC/B,YAAI,KAAK,OAAO,IAAI;AAChB,iBAAO;AAAA,QACX;AAAA,MACJ;AACA,aAAO;AAAA,IACX;AAAA,IAEA,YAAY,OAAO,IAAY,SAAyB;AACpD,iBAAW,CAAC,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG;AACzC,YAAI,KAAK,OAAO,IAAI;AAChB,gBAAM,cAAc,EAAE,GAAG,MAAM,GAAG,KAAK;AACvC,gBAAM,IAAI,OAAO,WAAW;AAC5B,iBAAO;AAAA,QACX;AAAA,MACJ;AACA,aAAO;AAAA,IACX;AAAA,IAEA,kBAAkB,OAAO,QAAgB,UAAkB,eAAuB;AAC9E,YAAM,YAAY,GAAG,QAAQ,IAAI,UAAU;AAC3C,eAAS,IAAI,WAAW,EAAE,QAAQ,UAAU,WAAW,CAAC;AAAA,IAC5D;AAAA,IAEA,oBAAoB,OAAO,KAAa,aAAqB;AACzD,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,WAAW,IAAI,GAAG;AAEjC,UAAI,CAAC,UAAU,MAAM,OAAO,WAAW;AACnC,cAAM,YAAY,EAAE,OAAO,GAAG,WAAW,MAAM,SAAS;AACxD,mBAAW,IAAI,KAAK,SAAS;AAC7B,eAAO;AAAA,MACX;AAEA,aAAO,SAAS;AAChB,aAAO;AAAA,IACX;AAAA,IAEA,cAAc,OAAO,QAAgB;AACjC,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,WAAW,IAAI,GAAG;AACjC,UAAI,CAAC,UAAU,MAAM,OAAO,UAAW,QAAO;AAC9C,aAAO;AAAA,IACX;AAAA,EACJ;AACJ;",
|
|
4
|
+
"sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates an in-memory database adapter for the auth engine.\r\n * This is useful for testing, prototyping, or when you don't need persistent storage.\r\n * All data is kept in memory and is lost when the server restarts.\r\n */\r\nexport function createMemoryAdapter<TUser extends User = User>(): AuthAdapter<TUser> {\r\n const users = new Map<string, TUser>();\r\n const accounts = new Map<string, { userId: string; provider: string; providerId: string }>();\r\n const rateLimits = new Map<string, { count: number; resetTime: number }>();\r\n\r\n return {\r\n createUser: async (data: any) => {\r\n // Auto-generate ID if not provided\r\n const id = data.id || Date.now().toString();\r\n const newUser = { ...data, id } as TUser;\r\n\r\n // Store using email as the primary lookup key\r\n users.set(newUser.email, newUser);\r\n return newUser;\r\n },\r\n\r\n findUserByEmail: async (email: string) => {\r\n return users.get(email) || null;\r\n },\r\n\r\n findUserById: async (id: string) => {\r\n for (const user of users.values()) {\r\n if (user.id === id) {\r\n return user;\r\n }\r\n }\r\n return null;\r\n },\r\n\r\n updateUser: async (id: string, data: Partial<TUser>) => {\r\n for (const [email, user] of users.entries()) {\r\n if (user.id === id) {\r\n const updatedUser = { ...user, ...data } as TUser;\r\n users.set(email, updatedUser);\r\n return updatedUser;\r\n }\r\n }\r\n return null;\r\n },\r\n\r\n linkOAuthAccount: async (userId: string, provider: string, providerId: string) => {\r\n const accountId = `${provider}_${providerId}`;\r\n accounts.set(accountId, { userId, provider, providerId });\r\n },\r\n\r\n incrementRateLimit: async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n const record = rateLimits.get(key);\r\n\r\n if (!record || now > record.resetTime) {\r\n const newRecord = { count: 1, resetTime: now + windowMs };\r\n rateLimits.set(key, newRecord);\r\n return newRecord;\r\n }\r\n\r\n record.count += 1;\r\n return record;\r\n },\r\n\r\n getRateLimit: async (key: string) => {\r\n const now = Date.now();\r\n const record = rateLimits.get(key);\r\n if (!record || now > record.resetTime) return null;\r\n return record;\r\n },\r\n\r\n invalidateSession: async (userId: string) => {\r\n for (const [email, user] of users.entries()) {\r\n if (user.id === userId) {\r\n const updatedUser = { ...user, sessionVersion: (user.sessionVersion || 0) + 1 } as TUser;\r\n users.set(email, updatedUser);\r\n return;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOO,SAAS,sBAAqE;AACjF,QAAM,QAAQ,oBAAI,IAAmB;AACrC,QAAM,WAAW,oBAAI,IAAsE;AAC3F,QAAM,aAAa,oBAAI,IAAkD;AAEzE,SAAO;AAAA,IACH,YAAY,OAAO,SAAc;AAE7B,YAAM,KAAK,KAAK,MAAM,KAAK,IAAI,EAAE,SAAS;AAC1C,YAAM,UAAU,EAAE,GAAG,MAAM,GAAG;AAG9B,YAAM,IAAI,QAAQ,OAAO,OAAO;AAChC,aAAO;AAAA,IACX;AAAA,IAEA,iBAAiB,OAAO,UAAkB;AACtC,aAAO,MAAM,IAAI,KAAK,KAAK;AAAA,IAC/B;AAAA,IAEA,cAAc,OAAO,OAAe;AAChC,iBAAW,QAAQ,MAAM,OAAO,GAAG;AAC/B,YAAI,KAAK,OAAO,IAAI;AAChB,iBAAO;AAAA,QACX;AAAA,MACJ;AACA,aAAO;AAAA,IACX;AAAA,IAEA,YAAY,OAAO,IAAY,SAAyB;AACpD,iBAAW,CAAC,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG;AACzC,YAAI,KAAK,OAAO,IAAI;AAChB,gBAAM,cAAc,EAAE,GAAG,MAAM,GAAG,KAAK;AACvC,gBAAM,IAAI,OAAO,WAAW;AAC5B,iBAAO;AAAA,QACX;AAAA,MACJ;AACA,aAAO;AAAA,IACX;AAAA,IAEA,kBAAkB,OAAO,QAAgB,UAAkB,eAAuB;AAC9E,YAAM,YAAY,GAAG,QAAQ,IAAI,UAAU;AAC3C,eAAS,IAAI,WAAW,EAAE,QAAQ,UAAU,WAAW,CAAC;AAAA,IAC5D;AAAA,IAEA,oBAAoB,OAAO,KAAa,aAAqB;AACzD,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,WAAW,IAAI,GAAG;AAEjC,UAAI,CAAC,UAAU,MAAM,OAAO,WAAW;AACnC,cAAM,YAAY,EAAE,OAAO,GAAG,WAAW,MAAM,SAAS;AACxD,mBAAW,IAAI,KAAK,SAAS;AAC7B,eAAO;AAAA,MACX;AAEA,aAAO,SAAS;AAChB,aAAO;AAAA,IACX;AAAA,IAEA,cAAc,OAAO,QAAgB;AACjC,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,WAAW,IAAI,GAAG;AACjC,UAAI,CAAC,UAAU,MAAM,OAAO,UAAW,QAAO;AAC9C,aAAO;AAAA,IACX;AAAA,IAEA,mBAAmB,OAAO,WAAmB;AACzC,iBAAW,CAAC,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG;AACzC,YAAI,KAAK,OAAO,QAAQ;AACpB,gBAAM,cAAc,EAAE,GAAG,MAAM,iBAAiB,KAAK,kBAAkB,KAAK,EAAE;AAC9E,gBAAM,IAAI,OAAO,WAAW;AAC5B;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/auth/adapters/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,IAAI,GAAG,IAAI,KAAK,WAAW,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/auth/adapters/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,IAAI,GAAG,IAAI,KAAK,WAAW,CAAC,KAAK,CAAC,CA4EnF"}
|
|
@@ -50,6 +50,15 @@ function createMemoryAdapter() {
|
|
|
50
50
|
const record = rateLimits.get(key);
|
|
51
51
|
if (!record || now > record.resetTime) return null;
|
|
52
52
|
return record;
|
|
53
|
+
},
|
|
54
|
+
invalidateSession: async (userId) => {
|
|
55
|
+
for (const [email, user] of users.entries()) {
|
|
56
|
+
if (user.id === userId) {
|
|
57
|
+
const updatedUser = { ...user, sessionVersion: (user.sessionVersion || 0) + 1 };
|
|
58
|
+
users.set(email, updatedUser);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
53
62
|
}
|
|
54
63
|
};
|
|
55
64
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/auth/adapters/memory.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates an in-memory database adapter for the auth engine.\r\n * This is useful for testing, prototyping, or when you don't need persistent storage.\r\n * All data is kept in memory and is lost when the server restarts.\r\n */\r\nexport function createMemoryAdapter<TUser extends User = User>(): AuthAdapter<TUser> {\r\n const users = new Map<string, TUser>();\r\n const accounts = new Map<string, { userId: string; provider: string; providerId: string }>();\r\n const rateLimits = new Map<string, { count: number; resetTime: number }>();\r\n\r\n return {\r\n createUser: async (data: any) => {\r\n // Auto-generate ID if not provided\r\n const id = data.id || Date.now().toString();\r\n const newUser = { ...data, id } as TUser;\r\n\r\n // Store using email as the primary lookup key\r\n users.set(newUser.email, newUser);\r\n return newUser;\r\n },\r\n\r\n findUserByEmail: async (email: string) => {\r\n return users.get(email) || null;\r\n },\r\n\r\n findUserById: async (id: string) => {\r\n for (const user of users.values()) {\r\n if (user.id === id) {\r\n return user;\r\n }\r\n }\r\n return null;\r\n },\r\n\r\n updateUser: async (id: string, data: Partial<TUser>) => {\r\n for (const [email, user] of users.entries()) {\r\n if (user.id === id) {\r\n const updatedUser = { ...user, ...data } as TUser;\r\n users.set(email, updatedUser);\r\n return updatedUser;\r\n }\r\n }\r\n return null;\r\n },\r\n\r\n linkOAuthAccount: async (userId: string, provider: string, providerId: string) => {\r\n const accountId = `${provider}_${providerId}`;\r\n accounts.set(accountId, { userId, provider, providerId });\r\n },\r\n\r\n incrementRateLimit: async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n const record = rateLimits.get(key);\r\n\r\n if (!record || now > record.resetTime) {\r\n const newRecord = { count: 1, resetTime: now + windowMs };\r\n rateLimits.set(key, newRecord);\r\n return newRecord;\r\n }\r\n\r\n record.count += 1;\r\n return record;\r\n },\r\n\r\n getRateLimit: async (key: string) => {\r\n const now = Date.now();\r\n const record = rateLimits.get(key);\r\n if (!record || now > record.resetTime) return null;\r\n return record;\r\n }\r\n };\r\n}\r\n"],
|
|
5
|
-
"mappings": "AAOO,SAAS,sBAAqE;AACjF,QAAM,QAAQ,oBAAI,IAAmB;AACrC,QAAM,WAAW,oBAAI,IAAsE;AAC3F,QAAM,aAAa,oBAAI,IAAkD;AAEzE,SAAO;AAAA,IACH,YAAY,OAAO,SAAc;AAE7B,YAAM,KAAK,KAAK,MAAM,KAAK,IAAI,EAAE,SAAS;AAC1C,YAAM,UAAU,EAAE,GAAG,MAAM,GAAG;AAG9B,YAAM,IAAI,QAAQ,OAAO,OAAO;AAChC,aAAO;AAAA,IACX;AAAA,IAEA,iBAAiB,OAAO,UAAkB;AACtC,aAAO,MAAM,IAAI,KAAK,KAAK;AAAA,IAC/B;AAAA,IAEA,cAAc,OAAO,OAAe;AAChC,iBAAW,QAAQ,MAAM,OAAO,GAAG;AAC/B,YAAI,KAAK,OAAO,IAAI;AAChB,iBAAO;AAAA,QACX;AAAA,MACJ;AACA,aAAO;AAAA,IACX;AAAA,IAEA,YAAY,OAAO,IAAY,SAAyB;AACpD,iBAAW,CAAC,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG;AACzC,YAAI,KAAK,OAAO,IAAI;AAChB,gBAAM,cAAc,EAAE,GAAG,MAAM,GAAG,KAAK;AACvC,gBAAM,IAAI,OAAO,WAAW;AAC5B,iBAAO;AAAA,QACX;AAAA,MACJ;AACA,aAAO;AAAA,IACX;AAAA,IAEA,kBAAkB,OAAO,QAAgB,UAAkB,eAAuB;AAC9E,YAAM,YAAY,GAAG,QAAQ,IAAI,UAAU;AAC3C,eAAS,IAAI,WAAW,EAAE,QAAQ,UAAU,WAAW,CAAC;AAAA,IAC5D;AAAA,IAEA,oBAAoB,OAAO,KAAa,aAAqB;AACzD,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,WAAW,IAAI,GAAG;AAEjC,UAAI,CAAC,UAAU,MAAM,OAAO,WAAW;AACnC,cAAM,YAAY,EAAE,OAAO,GAAG,WAAW,MAAM,SAAS;AACxD,mBAAW,IAAI,KAAK,SAAS;AAC7B,eAAO;AAAA,MACX;AAEA,aAAO,SAAS;AAChB,aAAO;AAAA,IACX;AAAA,IAEA,cAAc,OAAO,QAAgB;AACjC,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,WAAW,IAAI,GAAG;AACjC,UAAI,CAAC,UAAU,MAAM,OAAO,UAAW,QAAO;AAC9C,aAAO;AAAA,IACX;AAAA,EACJ;AACJ;",
|
|
4
|
+
"sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates an in-memory database adapter for the auth engine.\r\n * This is useful for testing, prototyping, or when you don't need persistent storage.\r\n * All data is kept in memory and is lost when the server restarts.\r\n */\r\nexport function createMemoryAdapter<TUser extends User = User>(): AuthAdapter<TUser> {\r\n const users = new Map<string, TUser>();\r\n const accounts = new Map<string, { userId: string; provider: string; providerId: string }>();\r\n const rateLimits = new Map<string, { count: number; resetTime: number }>();\r\n\r\n return {\r\n createUser: async (data: any) => {\r\n // Auto-generate ID if not provided\r\n const id = data.id || Date.now().toString();\r\n const newUser = { ...data, id } as TUser;\r\n\r\n // Store using email as the primary lookup key\r\n users.set(newUser.email, newUser);\r\n return newUser;\r\n },\r\n\r\n findUserByEmail: async (email: string) => {\r\n return users.get(email) || null;\r\n },\r\n\r\n findUserById: async (id: string) => {\r\n for (const user of users.values()) {\r\n if (user.id === id) {\r\n return user;\r\n }\r\n }\r\n return null;\r\n },\r\n\r\n updateUser: async (id: string, data: Partial<TUser>) => {\r\n for (const [email, user] of users.entries()) {\r\n if (user.id === id) {\r\n const updatedUser = { ...user, ...data } as TUser;\r\n users.set(email, updatedUser);\r\n return updatedUser;\r\n }\r\n }\r\n return null;\r\n },\r\n\r\n linkOAuthAccount: async (userId: string, provider: string, providerId: string) => {\r\n const accountId = `${provider}_${providerId}`;\r\n accounts.set(accountId, { userId, provider, providerId });\r\n },\r\n\r\n incrementRateLimit: async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n const record = rateLimits.get(key);\r\n\r\n if (!record || now > record.resetTime) {\r\n const newRecord = { count: 1, resetTime: now + windowMs };\r\n rateLimits.set(key, newRecord);\r\n return newRecord;\r\n }\r\n\r\n record.count += 1;\r\n return record;\r\n },\r\n\r\n getRateLimit: async (key: string) => {\r\n const now = Date.now();\r\n const record = rateLimits.get(key);\r\n if (!record || now > record.resetTime) return null;\r\n return record;\r\n },\r\n\r\n invalidateSession: async (userId: string) => {\r\n for (const [email, user] of users.entries()) {\r\n if (user.id === userId) {\r\n const updatedUser = { ...user, sessionVersion: (user.sessionVersion || 0) + 1 } as TUser;\r\n users.set(email, updatedUser);\r\n return;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n"],
|
|
5
|
+
"mappings": "AAOO,SAAS,sBAAqE;AACjF,QAAM,QAAQ,oBAAI,IAAmB;AACrC,QAAM,WAAW,oBAAI,IAAsE;AAC3F,QAAM,aAAa,oBAAI,IAAkD;AAEzE,SAAO;AAAA,IACH,YAAY,OAAO,SAAc;AAE7B,YAAM,KAAK,KAAK,MAAM,KAAK,IAAI,EAAE,SAAS;AAC1C,YAAM,UAAU,EAAE,GAAG,MAAM,GAAG;AAG9B,YAAM,IAAI,QAAQ,OAAO,OAAO;AAChC,aAAO;AAAA,IACX;AAAA,IAEA,iBAAiB,OAAO,UAAkB;AACtC,aAAO,MAAM,IAAI,KAAK,KAAK;AAAA,IAC/B;AAAA,IAEA,cAAc,OAAO,OAAe;AAChC,iBAAW,QAAQ,MAAM,OAAO,GAAG;AAC/B,YAAI,KAAK,OAAO,IAAI;AAChB,iBAAO;AAAA,QACX;AAAA,MACJ;AACA,aAAO;AAAA,IACX;AAAA,IAEA,YAAY,OAAO,IAAY,SAAyB;AACpD,iBAAW,CAAC,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG;AACzC,YAAI,KAAK,OAAO,IAAI;AAChB,gBAAM,cAAc,EAAE,GAAG,MAAM,GAAG,KAAK;AACvC,gBAAM,IAAI,OAAO,WAAW;AAC5B,iBAAO;AAAA,QACX;AAAA,MACJ;AACA,aAAO;AAAA,IACX;AAAA,IAEA,kBAAkB,OAAO,QAAgB,UAAkB,eAAuB;AAC9E,YAAM,YAAY,GAAG,QAAQ,IAAI,UAAU;AAC3C,eAAS,IAAI,WAAW,EAAE,QAAQ,UAAU,WAAW,CAAC;AAAA,IAC5D;AAAA,IAEA,oBAAoB,OAAO,KAAa,aAAqB;AACzD,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,WAAW,IAAI,GAAG;AAEjC,UAAI,CAAC,UAAU,MAAM,OAAO,WAAW;AACnC,cAAM,YAAY,EAAE,OAAO,GAAG,WAAW,MAAM,SAAS;AACxD,mBAAW,IAAI,KAAK,SAAS;AAC7B,eAAO;AAAA,MACX;AAEA,aAAO,SAAS;AAChB,aAAO;AAAA,IACX;AAAA,IAEA,cAAc,OAAO,QAAgB;AACjC,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,WAAW,IAAI,GAAG;AACjC,UAAI,CAAC,UAAU,MAAM,OAAO,UAAW,QAAO;AAC9C,aAAO;AAAA,IACX;AAAA,IAEA,mBAAmB,OAAO,WAAmB;AACzC,iBAAW,CAAC,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG;AACzC,YAAI,KAAK,OAAO,QAAQ;AACpB,gBAAM,cAAc,EAAE,GAAG,MAAM,iBAAiB,KAAK,kBAAkB,KAAK,EAAE;AAC9E,gBAAM,IAAI,OAAO,WAAW;AAC5B;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -61,6 +61,11 @@ function createMongoAdapter(model, rateLimitModel) {
|
|
|
61
61
|
oauthProvider: provider,
|
|
62
62
|
oauthId: providerId
|
|
63
63
|
});
|
|
64
|
+
},
|
|
65
|
+
async invalidateSession(userId) {
|
|
66
|
+
await model.findByIdAndUpdate(userId, {
|
|
67
|
+
$inc: { sessionVersion: 1 }
|
|
68
|
+
});
|
|
64
69
|
}
|
|
65
70
|
};
|
|
66
71
|
if (rateLimitModel) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/auth/adapters/mongoose.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates a pre-configured Mongoose model for rate limiting.\r\n * @param mongoose The mongoose instance to use for schema creation.\r\n */\r\nexport function createRateLimitModel(mongoose: any) {\r\n const schema = new mongoose.Schema({\r\n key: { type: String, required: true, unique: true },\r\n count: { type: Number, required: true, default: 1 },\r\n resetTime: { type: Number, required: true },\r\n expiresAt: { type: Date, required: true, expires: 0 }\r\n });\r\n return mongoose.models.KroxtRateLimit || mongoose.model('KroxtRateLimit', schema);\r\n}\r\n\r\n/**\r\n * Creates a MongoDB adapter using a Mongoose model.\r\n * \r\n * @param model - A Mongoose model instance (e.g., User model).\r\n * @param rateLimitModel - An optional Mongoose model for rate limiting tracking.\r\n * @returns An AuthAdapter compliant object.\r\n */\r\nexport function createMongoAdapter<TUser extends User = User>(model: any, rateLimitModel?: any): AuthAdapter<TUser> {\r\n const adapter: AuthAdapter<TUser> = {\r\n async createUser(data: any) {\r\n const user = await model.create(data);\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async findUserByEmail(email: string) {\r\n const user = await model.findOne({ email });\r\n if (!user) return null;\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async findUserById(id: string) {\r\n const user = await model.findById(id);\r\n if (!user) return null;\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async updateUser(id: string, data: Partial<TUser>) {\r\n const user = await model.findByIdAndUpdate(id, data, { new: true });\r\n if (!user) return null;\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async linkOAuthAccount(userId: string, provider: string, providerId: string) {\r\n await model.findByIdAndUpdate(userId, {\r\n oauthProvider: provider,\r\n oauthId: providerId,\r\n });\r\n },\r\n };\r\n\r\n if (rateLimitModel) {\r\n adapter.incrementRateLimit = async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n let record = await rateLimitModel.findOne({ key });\r\n \r\n if (!record || now > record.resetTime) {\r\n record = await rateLimitModel.findOneAndUpdate(\r\n { key },\r\n { count: 1, resetTime: now + windowMs, expiresAt: new Date(now + windowMs) },\r\n { upsert: true, new: true, setDefaultsOnInsert: true }\r\n );\r\n } else {\r\n record = await rateLimitModel.findOneAndUpdate(\r\n { key },\r\n { $inc: { count: 1 } },\r\n { new: true }\r\n );\r\n }\r\n\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n\r\n adapter.getRateLimit = async (key: string) => {\r\n const now = Date.now();\r\n const record = await rateLimitModel.findOne({ key });\r\n if (!record || now > record.resetTime) return null;\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n }\r\n\r\n return adapter;\r\n}\r\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates a pre-configured Mongoose model for rate limiting.\r\n * @param mongoose The mongoose instance to use for schema creation.\r\n */\r\nexport function createRateLimitModel(mongoose: any) {\r\n const schema = new mongoose.Schema({\r\n key: { type: String, required: true, unique: true },\r\n count: { type: Number, required: true, default: 1 },\r\n resetTime: { type: Number, required: true },\r\n expiresAt: { type: Date, required: true, expires: 0 }\r\n });\r\n return mongoose.models.KroxtRateLimit || mongoose.model('KroxtRateLimit', schema);\r\n}\r\n\r\n/**\r\n * Creates a MongoDB adapter using a Mongoose model.\r\n * \r\n * @param model - A Mongoose model instance (e.g., User model).\r\n * @param rateLimitModel - An optional Mongoose model for rate limiting tracking.\r\n * @returns An AuthAdapter compliant object.\r\n */\r\nexport function createMongoAdapter<TUser extends User = User>(model: any, rateLimitModel?: any): AuthAdapter<TUser> {\r\n const adapter: AuthAdapter<TUser> = {\r\n async createUser(data: any) {\r\n const user = await model.create(data);\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async findUserByEmail(email: string) {\r\n const user = await model.findOne({ email });\r\n if (!user) return null;\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async findUserById(id: string) {\r\n const user = await model.findById(id);\r\n if (!user) return null;\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async updateUser(id: string, data: Partial<TUser>) {\r\n const user = await model.findByIdAndUpdate(id, data, { new: true });\r\n if (!user) return null;\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async linkOAuthAccount(userId: string, provider: string, providerId: string) {\r\n await model.findByIdAndUpdate(userId, {\r\n oauthProvider: provider,\r\n oauthId: providerId,\r\n });\r\n },\r\n\r\n async invalidateSession(userId: string) {\r\n await model.findByIdAndUpdate(userId, { \r\n $inc: { sessionVersion: 1 } \r\n });\r\n },\r\n };\r\n\r\n if (rateLimitModel) {\r\n adapter.incrementRateLimit = async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n let record = await rateLimitModel.findOne({ key });\r\n \r\n if (!record || now > record.resetTime) {\r\n record = await rateLimitModel.findOneAndUpdate(\r\n { key },\r\n { count: 1, resetTime: now + windowMs, expiresAt: new Date(now + windowMs) },\r\n { upsert: true, new: true, setDefaultsOnInsert: true }\r\n );\r\n } else {\r\n record = await rateLimitModel.findOneAndUpdate(\r\n { key },\r\n { $inc: { count: 1 } },\r\n { new: true }\r\n );\r\n }\r\n\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n\r\n adapter.getRateLimit = async (key: string) => {\r\n const now = Date.now();\r\n const record = await rateLimitModel.findOne({ key });\r\n if (!record || now > record.resetTime) return null;\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n }\r\n\r\n return adapter;\r\n}\r\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMO,SAAS,qBAAqB,UAAe;AAClD,QAAM,SAAS,IAAI,SAAS,OAAO;AAAA,IACjC,KAAK,EAAE,MAAM,QAAQ,UAAU,MAAM,QAAQ,KAAK;AAAA,IAClD,OAAO,EAAE,MAAM,QAAQ,UAAU,MAAM,SAAS,EAAE;AAAA,IAClD,WAAW,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IAC1C,WAAW,EAAE,MAAM,MAAM,UAAU,MAAM,SAAS,EAAE;AAAA,EACtD,CAAC;AACD,SAAO,SAAS,OAAO,kBAAkB,SAAS,MAAM,kBAAkB,MAAM;AAClF;AASO,SAAS,mBAA8C,OAAY,gBAA0C;AAClH,QAAM,UAA8B;AAAA,IAClC,MAAM,WAAW,MAAW;AAC1B,YAAM,OAAO,MAAM,MAAM,OAAO,IAAI;AACpC,YAAM,MAAM,KAAK,SAAS;AAC1B,aAAO,EAAE,GAAG,KAAK,IAAI,IAAI,IAAI,SAAS,EAAE;AAAA,IAC1C;AAAA,IAEA,MAAM,gBAAgB,OAAe;AACnC,YAAM,OAAO,MAAM,MAAM,QAAQ,EAAE,MAAM,CAAC;AAC1C,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,MAAM,KAAK,SAAS;AAC1B,aAAO,EAAE,GAAG,KAAK,IAAI,IAAI,IAAI,SAAS,EAAE;AAAA,IAC1C;AAAA,IAEA,MAAM,aAAa,IAAY;AAC7B,YAAM,OAAO,MAAM,MAAM,SAAS,EAAE;AACpC,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,MAAM,KAAK,SAAS;AAC1B,aAAO,EAAE,GAAG,KAAK,IAAI,IAAI,IAAI,SAAS,EAAE;AAAA,IAC1C;AAAA,IAEA,MAAM,WAAW,IAAY,MAAsB;AACjD,YAAM,OAAO,MAAM,MAAM,kBAAkB,IAAI,MAAM,EAAE,KAAK,KAAK,CAAC;AAClE,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,MAAM,KAAK,SAAS;AAC1B,aAAO,EAAE,GAAG,KAAK,IAAI,IAAI,IAAI,SAAS,EAAE;AAAA,IAC1C;AAAA,IAEA,MAAM,iBAAiB,QAAgB,UAAkB,YAAoB;AAC3E,YAAM,MAAM,kBAAkB,QAAQ;AAAA,QACpC,eAAe;AAAA,QACf,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,kBAAkB,QAAgB;AACtC,YAAM,MAAM,kBAAkB,QAAQ;AAAA,QACpC,MAAM,EAAE,gBAAgB,EAAE;AAAA,MAC5B,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,gBAAgB;AAClB,YAAQ,qBAAqB,OAAO,KAAa,aAAqB;AACpE,YAAM,MAAM,KAAK,IAAI;AACrB,UAAI,SAAS,MAAM,eAAe,QAAQ,EAAE,IAAI,CAAC;AAEjD,UAAI,CAAC,UAAU,MAAM,OAAO,WAAW;AACrC,iBAAS,MAAM,eAAe;AAAA,UAC5B,EAAE,IAAI;AAAA,UACN,EAAE,OAAO,GAAG,WAAW,MAAM,UAAU,WAAW,IAAI,KAAK,MAAM,QAAQ,EAAE;AAAA,UAC3E,EAAE,QAAQ,MAAM,KAAK,MAAM,qBAAqB,KAAK;AAAA,QACvD;AAAA,MACF,OAAO;AACL,iBAAS,MAAM,eAAe;AAAA,UAC5B,EAAE,IAAI;AAAA,UACN,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;AAAA,UACrB,EAAE,KAAK,KAAK;AAAA,QACd;AAAA,MACF;AAEA,aAAO,EAAE,OAAO,OAAO,OAAO,WAAW,OAAO,UAAU;AAAA,IAC5D;AAEA,YAAQ,eAAe,OAAO,QAAgB;AAC5C,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,MAAM,eAAe,QAAQ,EAAE,IAAI,CAAC;AACnD,UAAI,CAAC,UAAU,MAAM,OAAO,UAAW,QAAO;AAC9C,aAAO,EAAE,OAAO,OAAO,OAAO,WAAW,OAAO,UAAU;AAAA,IAC5D;AAAA,EACF;AAEA,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongoose.d.ts","sourceRoot":"","sources":["../../../src/auth/adapters/mongoose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEpD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,GAAG,OAQjD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,SAAS,IAAI,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,EAAE,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"mongoose.d.ts","sourceRoot":"","sources":["../../../src/auth/adapters/mongoose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEpD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,GAAG,OAQjD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,SAAS,IAAI,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,EAAE,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,CA0ElH"}
|
|
@@ -37,6 +37,11 @@ function createMongoAdapter(model, rateLimitModel) {
|
|
|
37
37
|
oauthProvider: provider,
|
|
38
38
|
oauthId: providerId
|
|
39
39
|
});
|
|
40
|
+
},
|
|
41
|
+
async invalidateSession(userId) {
|
|
42
|
+
await model.findByIdAndUpdate(userId, {
|
|
43
|
+
$inc: { sessionVersion: 1 }
|
|
44
|
+
});
|
|
40
45
|
}
|
|
41
46
|
};
|
|
42
47
|
if (rateLimitModel) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/auth/adapters/mongoose.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates a pre-configured Mongoose model for rate limiting.\r\n * @param mongoose The mongoose instance to use for schema creation.\r\n */\r\nexport function createRateLimitModel(mongoose: any) {\r\n const schema = new mongoose.Schema({\r\n key: { type: String, required: true, unique: true },\r\n count: { type: Number, required: true, default: 1 },\r\n resetTime: { type: Number, required: true },\r\n expiresAt: { type: Date, required: true, expires: 0 }\r\n });\r\n return mongoose.models.KroxtRateLimit || mongoose.model('KroxtRateLimit', schema);\r\n}\r\n\r\n/**\r\n * Creates a MongoDB adapter using a Mongoose model.\r\n * \r\n * @param model - A Mongoose model instance (e.g., User model).\r\n * @param rateLimitModel - An optional Mongoose model for rate limiting tracking.\r\n * @returns An AuthAdapter compliant object.\r\n */\r\nexport function createMongoAdapter<TUser extends User = User>(model: any, rateLimitModel?: any): AuthAdapter<TUser> {\r\n const adapter: AuthAdapter<TUser> = {\r\n async createUser(data: any) {\r\n const user = await model.create(data);\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async findUserByEmail(email: string) {\r\n const user = await model.findOne({ email });\r\n if (!user) return null;\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async findUserById(id: string) {\r\n const user = await model.findById(id);\r\n if (!user) return null;\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async updateUser(id: string, data: Partial<TUser>) {\r\n const user = await model.findByIdAndUpdate(id, data, { new: true });\r\n if (!user) return null;\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async linkOAuthAccount(userId: string, provider: string, providerId: string) {\r\n await model.findByIdAndUpdate(userId, {\r\n oauthProvider: provider,\r\n oauthId: providerId,\r\n });\r\n },\r\n };\r\n\r\n if (rateLimitModel) {\r\n adapter.incrementRateLimit = async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n let record = await rateLimitModel.findOne({ key });\r\n \r\n if (!record || now > record.resetTime) {\r\n record = await rateLimitModel.findOneAndUpdate(\r\n { key },\r\n { count: 1, resetTime: now + windowMs, expiresAt: new Date(now + windowMs) },\r\n { upsert: true, new: true, setDefaultsOnInsert: true }\r\n );\r\n } else {\r\n record = await rateLimitModel.findOneAndUpdate(\r\n { key },\r\n { $inc: { count: 1 } },\r\n { new: true }\r\n );\r\n }\r\n\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n\r\n adapter.getRateLimit = async (key: string) => {\r\n const now = Date.now();\r\n const record = await rateLimitModel.findOne({ key });\r\n if (!record || now > record.resetTime) return null;\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n }\r\n\r\n return adapter;\r\n}\r\n"],
|
|
5
|
-
"mappings": "AAMO,SAAS,qBAAqB,UAAe;AAClD,QAAM,SAAS,IAAI,SAAS,OAAO;AAAA,IACjC,KAAK,EAAE,MAAM,QAAQ,UAAU,MAAM,QAAQ,KAAK;AAAA,IAClD,OAAO,EAAE,MAAM,QAAQ,UAAU,MAAM,SAAS,EAAE;AAAA,IAClD,WAAW,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IAC1C,WAAW,EAAE,MAAM,MAAM,UAAU,MAAM,SAAS,EAAE;AAAA,EACtD,CAAC;AACD,SAAO,SAAS,OAAO,kBAAkB,SAAS,MAAM,kBAAkB,MAAM;AAClF;AASO,SAAS,mBAA8C,OAAY,gBAA0C;AAClH,QAAM,UAA8B;AAAA,IAClC,MAAM,WAAW,MAAW;AAC1B,YAAM,OAAO,MAAM,MAAM,OAAO,IAAI;AACpC,YAAM,MAAM,KAAK,SAAS;AAC1B,aAAO,EAAE,GAAG,KAAK,IAAI,IAAI,IAAI,SAAS,EAAE;AAAA,IAC1C;AAAA,IAEA,MAAM,gBAAgB,OAAe;AACnC,YAAM,OAAO,MAAM,MAAM,QAAQ,EAAE,MAAM,CAAC;AAC1C,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,MAAM,KAAK,SAAS;AAC1B,aAAO,EAAE,GAAG,KAAK,IAAI,IAAI,IAAI,SAAS,EAAE;AAAA,IAC1C;AAAA,IAEA,MAAM,aAAa,IAAY;AAC7B,YAAM,OAAO,MAAM,MAAM,SAAS,EAAE;AACpC,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,MAAM,KAAK,SAAS;AAC1B,aAAO,EAAE,GAAG,KAAK,IAAI,IAAI,IAAI,SAAS,EAAE;AAAA,IAC1C;AAAA,IAEA,MAAM,WAAW,IAAY,MAAsB;AACjD,YAAM,OAAO,MAAM,MAAM,kBAAkB,IAAI,MAAM,EAAE,KAAK,KAAK,CAAC;AAClE,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,MAAM,KAAK,SAAS;AAC1B,aAAO,EAAE,GAAG,KAAK,IAAI,IAAI,IAAI,SAAS,EAAE;AAAA,IAC1C;AAAA,IAEA,MAAM,iBAAiB,QAAgB,UAAkB,YAAoB;AAC3E,YAAM,MAAM,kBAAkB,QAAQ;AAAA,QACpC,eAAe;AAAA,QACf,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,gBAAgB;AAClB,YAAQ,qBAAqB,OAAO,KAAa,aAAqB;AACpE,YAAM,MAAM,KAAK,IAAI;AACrB,UAAI,SAAS,MAAM,eAAe,QAAQ,EAAE,IAAI,CAAC;AAEjD,UAAI,CAAC,UAAU,MAAM,OAAO,WAAW;AACrC,iBAAS,MAAM,eAAe;AAAA,UAC5B,EAAE,IAAI;AAAA,UACN,EAAE,OAAO,GAAG,WAAW,MAAM,UAAU,WAAW,IAAI,KAAK,MAAM,QAAQ,EAAE;AAAA,UAC3E,EAAE,QAAQ,MAAM,KAAK,MAAM,qBAAqB,KAAK;AAAA,QACvD;AAAA,MACF,OAAO;AACL,iBAAS,MAAM,eAAe;AAAA,UAC5B,EAAE,IAAI;AAAA,UACN,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;AAAA,UACrB,EAAE,KAAK,KAAK;AAAA,QACd;AAAA,MACF;AAEA,aAAO,EAAE,OAAO,OAAO,OAAO,WAAW,OAAO,UAAU;AAAA,IAC5D;AAEA,YAAQ,eAAe,OAAO,QAAgB;AAC5C,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,MAAM,eAAe,QAAQ,EAAE,IAAI,CAAC;AACnD,UAAI,CAAC,UAAU,MAAM,OAAO,UAAW,QAAO;AAC9C,aAAO,EAAE,OAAO,OAAO,OAAO,WAAW,OAAO,UAAU;AAAA,IAC5D;AAAA,EACF;AAEA,SAAO;AACT;",
|
|
4
|
+
"sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates a pre-configured Mongoose model for rate limiting.\r\n * @param mongoose The mongoose instance to use for schema creation.\r\n */\r\nexport function createRateLimitModel(mongoose: any) {\r\n const schema = new mongoose.Schema({\r\n key: { type: String, required: true, unique: true },\r\n count: { type: Number, required: true, default: 1 },\r\n resetTime: { type: Number, required: true },\r\n expiresAt: { type: Date, required: true, expires: 0 }\r\n });\r\n return mongoose.models.KroxtRateLimit || mongoose.model('KroxtRateLimit', schema);\r\n}\r\n\r\n/**\r\n * Creates a MongoDB adapter using a Mongoose model.\r\n * \r\n * @param model - A Mongoose model instance (e.g., User model).\r\n * @param rateLimitModel - An optional Mongoose model for rate limiting tracking.\r\n * @returns An AuthAdapter compliant object.\r\n */\r\nexport function createMongoAdapter<TUser extends User = User>(model: any, rateLimitModel?: any): AuthAdapter<TUser> {\r\n const adapter: AuthAdapter<TUser> = {\r\n async createUser(data: any) {\r\n const user = await model.create(data);\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async findUserByEmail(email: string) {\r\n const user = await model.findOne({ email });\r\n if (!user) return null;\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async findUserById(id: string) {\r\n const user = await model.findById(id);\r\n if (!user) return null;\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async updateUser(id: string, data: Partial<TUser>) {\r\n const user = await model.findByIdAndUpdate(id, data, { new: true });\r\n if (!user) return null;\r\n const obj = user.toObject();\r\n return { ...obj, id: obj._id.toString() } as TUser;\r\n },\r\n\r\n async linkOAuthAccount(userId: string, provider: string, providerId: string) {\r\n await model.findByIdAndUpdate(userId, {\r\n oauthProvider: provider,\r\n oauthId: providerId,\r\n });\r\n },\r\n\r\n async invalidateSession(userId: string) {\r\n await model.findByIdAndUpdate(userId, { \r\n $inc: { sessionVersion: 1 } \r\n });\r\n },\r\n };\r\n\r\n if (rateLimitModel) {\r\n adapter.incrementRateLimit = async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n let record = await rateLimitModel.findOne({ key });\r\n \r\n if (!record || now > record.resetTime) {\r\n record = await rateLimitModel.findOneAndUpdate(\r\n { key },\r\n { count: 1, resetTime: now + windowMs, expiresAt: new Date(now + windowMs) },\r\n { upsert: true, new: true, setDefaultsOnInsert: true }\r\n );\r\n } else {\r\n record = await rateLimitModel.findOneAndUpdate(\r\n { key },\r\n { $inc: { count: 1 } },\r\n { new: true }\r\n );\r\n }\r\n\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n\r\n adapter.getRateLimit = async (key: string) => {\r\n const now = Date.now();\r\n const record = await rateLimitModel.findOne({ key });\r\n if (!record || now > record.resetTime) return null;\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n }\r\n\r\n return adapter;\r\n}\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -55,6 +55,14 @@ function createPrismaAdapter(model, rateLimitModel) {
|
|
|
55
55
|
oauthId: providerId
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
|
+
},
|
|
59
|
+
async invalidateSession(id) {
|
|
60
|
+
await model.update({
|
|
61
|
+
where: { id },
|
|
62
|
+
data: {
|
|
63
|
+
sessionVersion: { increment: 1 }
|
|
64
|
+
}
|
|
65
|
+
});
|
|
58
66
|
}
|
|
59
67
|
};
|
|
60
68
|
if (rateLimitModel) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/auth/adapters/prisma.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates a Prisma adapter using a Prisma delegate (e.g., prisma.user).\r\n * \r\n * Works with any Prisma-supported database by using the standard\r\n * Prisma delegate operations (findUnique, create, update).\r\n * \r\n * @param model - A Prisma delegate instance (e.g., prisma.user).\r\n * @param rateLimitModel - An optional Prisma delegate for rate limit tracking.\r\n * @returns An AuthAdapter compliant object.\r\n */\r\nexport function createPrismaAdapter<TUser extends User = User>(model: any, rateLimitModel?: any): AuthAdapter<TUser> {\r\n const adapter: AuthAdapter<TUser> = {\r\n async createUser(data: any) {\r\n const dataToSave = { id: data.id || globalThis.crypto.randomUUID(), ...data };\r\n const user = await model.create({ data: dataToSave });\r\n return user as TUser;\r\n },\r\n\r\n async findUserByEmail(email: string) {\r\n const user = await model.findUnique({\r\n where: { email },\r\n });\r\n return user as TUser | null;\r\n },\r\n\r\n async findUserById(id: string) {\r\n const user = await model.findUnique({\r\n where: { id },\r\n });\r\n return user as TUser | null;\r\n },\r\n\r\n async updateUser(id: string, data: Partial<TUser>) {\r\n const user = await model.update({\r\n where: { id },\r\n data,\r\n });\r\n return user as TUser | null;\r\n },\r\n\r\n async linkOAuthAccount(userId: string, provider: string, providerId: string) {\r\n await model.update({\r\n where: { id: userId },\r\n data: {\r\n oauthProvider: provider,\r\n oauthId: providerId,\r\n },\r\n });\r\n },\r\n };\r\n\r\n if (rateLimitModel) {\r\n adapter.incrementRateLimit = async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n const record = await rateLimitModel.findUnique({ where: { key } });\r\n \r\n if (!record || now > record.resetTime) {\r\n const newRecord = await rateLimitModel.upsert({\r\n where: { key },\r\n update: { count: 1, resetTime: now + windowMs },\r\n create: { key, count: 1, resetTime: now + windowMs }\r\n });\r\n return { count: newRecord.count, resetTime: newRecord.resetTime };\r\n } else {\r\n const updated = await rateLimitModel.update({\r\n where: { key },\r\n data: { count: { increment: 1 } }\r\n });\r\n return { count: updated.count, resetTime: updated.resetTime };\r\n }\r\n };\r\n\r\n adapter.getRateLimit = async (key: string) => {\r\n const now = Date.now();\r\n const record = await rateLimitModel.findUnique({ where: { key } });\r\n if (!record || now > record.resetTime) return null;\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n }\r\n\r\n return adapter;\r\n}\r\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAYO,SAAS,oBAA+C,OAAY,gBAA0C;AACnH,QAAM,UAA8B;AAAA,IAClC,MAAM,WAAW,MAAW;AAC1B,YAAM,aAAa,EAAE,IAAI,KAAK,MAAM,WAAW,OAAO,WAAW,GAAG,GAAG,KAAK;AAC5E,YAAM,OAAO,MAAM,MAAM,OAAO,EAAE,MAAM,WAAW,CAAC;AACpD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,gBAAgB,OAAe;AACnC,YAAM,OAAO,MAAM,MAAM,WAAW;AAAA,QAClC,OAAO,EAAE,MAAM;AAAA,MACjB,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,aAAa,IAAY;AAC7B,YAAM,OAAO,MAAM,MAAM,WAAW;AAAA,QAClC,OAAO,EAAE,GAAG;AAAA,MACd,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,WAAW,IAAY,MAAsB;AACjD,YAAM,OAAO,MAAM,MAAM,OAAO;AAAA,QAC9B,OAAO,EAAE,GAAG;AAAA,QACZ;AAAA,MACF,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,iBAAiB,QAAgB,UAAkB,YAAoB;AAC3E,YAAM,MAAM,OAAO;AAAA,QACjB,OAAO,EAAE,IAAI,OAAO;AAAA,QACpB,MAAM;AAAA,UACJ,eAAe;AAAA,UACf,SAAS;AAAA,QACX;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,gBAAgB;AAClB,YAAQ,qBAAqB,OAAO,KAAa,aAAqB;AACpE,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,MAAM,eAAe,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAEjE,UAAI,CAAC,UAAU,MAAM,OAAO,WAAW;AACnC,cAAM,YAAY,MAAM,eAAe,OAAO;AAAA,UAC1C,OAAO,EAAE,IAAI;AAAA,UACb,QAAQ,EAAE,OAAO,GAAG,WAAW,MAAM,SAAS;AAAA,UAC9C,QAAQ,EAAE,KAAK,OAAO,GAAG,WAAW,MAAM,SAAS;AAAA,QACvD,CAAC;AACD,eAAO,EAAE,OAAO,UAAU,OAAO,WAAW,UAAU,UAAU;AAAA,MACpE,OAAO;AACH,cAAM,UAAU,MAAM,eAAe,OAAO;AAAA,UACxC,OAAO,EAAE,IAAI;AAAA,UACb,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE;AAAA,QACpC,CAAC;AACD,eAAO,EAAE,OAAO,QAAQ,OAAO,WAAW,QAAQ,UAAU;AAAA,MAChE;AAAA,IACF;AAEA,YAAQ,eAAe,OAAO,QAAgB;AAC5C,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,MAAM,eAAe,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACjE,UAAI,CAAC,UAAU,MAAM,OAAO,UAAW,QAAO;AAC9C,aAAO,EAAE,OAAO,OAAO,OAAO,WAAW,OAAO,UAAU;AAAA,IAC5D;AAAA,EACF;AAEA,SAAO;AACT;",
|
|
4
|
+
"sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates a Prisma adapter using a Prisma delegate (e.g., prisma.user).\r\n * \r\n * Works with any Prisma-supported database by using the standard\r\n * Prisma delegate operations (findUnique, create, update).\r\n * \r\n * @param model - A Prisma delegate instance (e.g., prisma.user).\r\n * @param rateLimitModel - An optional Prisma delegate for rate limit tracking.\r\n * @returns An AuthAdapter compliant object.\r\n */\r\nexport function createPrismaAdapter<TUser extends User = User>(model: any, rateLimitModel?: any): AuthAdapter<TUser> {\r\n const adapter: AuthAdapter<TUser> = {\r\n async createUser(data: any) {\r\n const dataToSave = { id: data.id || globalThis.crypto.randomUUID(), ...data };\r\n const user = await model.create({ data: dataToSave });\r\n return user as TUser;\r\n },\r\n\r\n async findUserByEmail(email: string) {\r\n const user = await model.findUnique({\r\n where: { email },\r\n });\r\n return user as TUser | null;\r\n },\r\n\r\n async findUserById(id: string) {\r\n const user = await model.findUnique({\r\n where: { id },\r\n });\r\n return user as TUser | null;\r\n },\r\n\r\n async updateUser(id: string, data: Partial<TUser>) {\r\n const user = await model.update({\r\n where: { id },\r\n data,\r\n });\r\n return user as TUser | null;\r\n },\r\n\r\n async linkOAuthAccount(userId: string, provider: string, providerId: string) {\r\n await model.update({\r\n where: { id: userId },\r\n data: {\r\n oauthProvider: provider,\r\n oauthId: providerId,\r\n },\r\n });\r\n },\r\n\r\n async invalidateSession(id: string) {\r\n await model.update({\r\n where: { id },\r\n data: {\r\n sessionVersion: { increment: 1 },\r\n },\r\n });\r\n },\r\n };\r\n\r\n if (rateLimitModel) {\r\n adapter.incrementRateLimit = async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n const record = await rateLimitModel.findUnique({ where: { key } });\r\n \r\n if (!record || now > record.resetTime) {\r\n const newRecord = await rateLimitModel.upsert({\r\n where: { key },\r\n update: { count: 1, resetTime: now + windowMs },\r\n create: { key, count: 1, resetTime: now + windowMs }\r\n });\r\n return { count: newRecord.count, resetTime: newRecord.resetTime };\r\n } else {\r\n const updated = await rateLimitModel.update({\r\n where: { key },\r\n data: { count: { increment: 1 } }\r\n });\r\n return { count: updated.count, resetTime: updated.resetTime };\r\n }\r\n };\r\n\r\n adapter.getRateLimit = async (key: string) => {\r\n const now = Date.now();\r\n const record = await rateLimitModel.findUnique({ where: { key } });\r\n if (!record || now > record.resetTime) return null;\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n }\r\n\r\n return adapter;\r\n}\r\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAYO,SAAS,oBAA+C,OAAY,gBAA0C;AACnH,QAAM,UAA8B;AAAA,IAClC,MAAM,WAAW,MAAW;AAC1B,YAAM,aAAa,EAAE,IAAI,KAAK,MAAM,WAAW,OAAO,WAAW,GAAG,GAAG,KAAK;AAC5E,YAAM,OAAO,MAAM,MAAM,OAAO,EAAE,MAAM,WAAW,CAAC;AACpD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,gBAAgB,OAAe;AACnC,YAAM,OAAO,MAAM,MAAM,WAAW;AAAA,QAClC,OAAO,EAAE,MAAM;AAAA,MACjB,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,aAAa,IAAY;AAC7B,YAAM,OAAO,MAAM,MAAM,WAAW;AAAA,QAClC,OAAO,EAAE,GAAG;AAAA,MACd,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,WAAW,IAAY,MAAsB;AACjD,YAAM,OAAO,MAAM,MAAM,OAAO;AAAA,QAC9B,OAAO,EAAE,GAAG;AAAA,QACZ;AAAA,MACF,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,iBAAiB,QAAgB,UAAkB,YAAoB;AAC3E,YAAM,MAAM,OAAO;AAAA,QACjB,OAAO,EAAE,IAAI,OAAO;AAAA,QACpB,MAAM;AAAA,UACJ,eAAe;AAAA,UACf,SAAS;AAAA,QACX;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,kBAAkB,IAAY;AAClC,YAAM,MAAM,OAAO;AAAA,QACjB,OAAO,EAAE,GAAG;AAAA,QACZ,MAAM;AAAA,UACJ,gBAAgB,EAAE,WAAW,EAAE;AAAA,QACjC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,gBAAgB;AAClB,YAAQ,qBAAqB,OAAO,KAAa,aAAqB;AACpE,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,MAAM,eAAe,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAEjE,UAAI,CAAC,UAAU,MAAM,OAAO,WAAW;AACnC,cAAM,YAAY,MAAM,eAAe,OAAO;AAAA,UAC1C,OAAO,EAAE,IAAI;AAAA,UACb,QAAQ,EAAE,OAAO,GAAG,WAAW,MAAM,SAAS;AAAA,UAC9C,QAAQ,EAAE,KAAK,OAAO,GAAG,WAAW,MAAM,SAAS;AAAA,QACvD,CAAC;AACD,eAAO,EAAE,OAAO,UAAU,OAAO,WAAW,UAAU,UAAU;AAAA,MACpE,OAAO;AACH,cAAM,UAAU,MAAM,eAAe,OAAO;AAAA,UACxC,OAAO,EAAE,IAAI;AAAA,UACb,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE;AAAA,QACpC,CAAC;AACD,eAAO,EAAE,OAAO,QAAQ,OAAO,WAAW,QAAQ,UAAU;AAAA,MAChE;AAAA,IACF;AAEA,YAAQ,eAAe,OAAO,QAAgB;AAC5C,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,MAAM,eAAe,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACjE,UAAI,CAAC,UAAU,MAAM,OAAO,UAAW,QAAO;AAC9C,aAAO,EAAE,OAAO,OAAO,OAAO,WAAW,OAAO,UAAU;AAAA,IAC5D;AAAA,EACF;AAEA,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../../src/auth/adapters/prisma.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,IAAI,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,EAAE,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../../src/auth/adapters/prisma.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,IAAI,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,EAAE,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,CAgFnH"}
|
|
@@ -32,6 +32,14 @@ function createPrismaAdapter(model, rateLimitModel) {
|
|
|
32
32
|
oauthId: providerId
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
|
+
},
|
|
36
|
+
async invalidateSession(id) {
|
|
37
|
+
await model.update({
|
|
38
|
+
where: { id },
|
|
39
|
+
data: {
|
|
40
|
+
sessionVersion: { increment: 1 }
|
|
41
|
+
}
|
|
42
|
+
});
|
|
35
43
|
}
|
|
36
44
|
};
|
|
37
45
|
if (rateLimitModel) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/auth/adapters/prisma.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates a Prisma adapter using a Prisma delegate (e.g., prisma.user).\r\n * \r\n * Works with any Prisma-supported database by using the standard\r\n * Prisma delegate operations (findUnique, create, update).\r\n * \r\n * @param model - A Prisma delegate instance (e.g., prisma.user).\r\n * @param rateLimitModel - An optional Prisma delegate for rate limit tracking.\r\n * @returns An AuthAdapter compliant object.\r\n */\r\nexport function createPrismaAdapter<TUser extends User = User>(model: any, rateLimitModel?: any): AuthAdapter<TUser> {\r\n const adapter: AuthAdapter<TUser> = {\r\n async createUser(data: any) {\r\n const dataToSave = { id: data.id || globalThis.crypto.randomUUID(), ...data };\r\n const user = await model.create({ data: dataToSave });\r\n return user as TUser;\r\n },\r\n\r\n async findUserByEmail(email: string) {\r\n const user = await model.findUnique({\r\n where: { email },\r\n });\r\n return user as TUser | null;\r\n },\r\n\r\n async findUserById(id: string) {\r\n const user = await model.findUnique({\r\n where: { id },\r\n });\r\n return user as TUser | null;\r\n },\r\n\r\n async updateUser(id: string, data: Partial<TUser>) {\r\n const user = await model.update({\r\n where: { id },\r\n data,\r\n });\r\n return user as TUser | null;\r\n },\r\n\r\n async linkOAuthAccount(userId: string, provider: string, providerId: string) {\r\n await model.update({\r\n where: { id: userId },\r\n data: {\r\n oauthProvider: provider,\r\n oauthId: providerId,\r\n },\r\n });\r\n },\r\n };\r\n\r\n if (rateLimitModel) {\r\n adapter.incrementRateLimit = async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n const record = await rateLimitModel.findUnique({ where: { key } });\r\n \r\n if (!record || now > record.resetTime) {\r\n const newRecord = await rateLimitModel.upsert({\r\n where: { key },\r\n update: { count: 1, resetTime: now + windowMs },\r\n create: { key, count: 1, resetTime: now + windowMs }\r\n });\r\n return { count: newRecord.count, resetTime: newRecord.resetTime };\r\n } else {\r\n const updated = await rateLimitModel.update({\r\n where: { key },\r\n data: { count: { increment: 1 } }\r\n });\r\n return { count: updated.count, resetTime: updated.resetTime };\r\n }\r\n };\r\n\r\n adapter.getRateLimit = async (key: string) => {\r\n const now = Date.now();\r\n const record = await rateLimitModel.findUnique({ where: { key } });\r\n if (!record || now > record.resetTime) return null;\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n }\r\n\r\n return adapter;\r\n}\r\n"],
|
|
5
|
-
"mappings": "AAYO,SAAS,oBAA+C,OAAY,gBAA0C;AACnH,QAAM,UAA8B;AAAA,IAClC,MAAM,WAAW,MAAW;AAC1B,YAAM,aAAa,EAAE,IAAI,KAAK,MAAM,WAAW,OAAO,WAAW,GAAG,GAAG,KAAK;AAC5E,YAAM,OAAO,MAAM,MAAM,OAAO,EAAE,MAAM,WAAW,CAAC;AACpD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,gBAAgB,OAAe;AACnC,YAAM,OAAO,MAAM,MAAM,WAAW;AAAA,QAClC,OAAO,EAAE,MAAM;AAAA,MACjB,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,aAAa,IAAY;AAC7B,YAAM,OAAO,MAAM,MAAM,WAAW;AAAA,QAClC,OAAO,EAAE,GAAG;AAAA,MACd,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,WAAW,IAAY,MAAsB;AACjD,YAAM,OAAO,MAAM,MAAM,OAAO;AAAA,QAC9B,OAAO,EAAE,GAAG;AAAA,QACZ;AAAA,MACF,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,iBAAiB,QAAgB,UAAkB,YAAoB;AAC3E,YAAM,MAAM,OAAO;AAAA,QACjB,OAAO,EAAE,IAAI,OAAO;AAAA,QACpB,MAAM;AAAA,UACJ,eAAe;AAAA,UACf,SAAS;AAAA,QACX;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,gBAAgB;AAClB,YAAQ,qBAAqB,OAAO,KAAa,aAAqB;AACpE,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,MAAM,eAAe,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAEjE,UAAI,CAAC,UAAU,MAAM,OAAO,WAAW;AACnC,cAAM,YAAY,MAAM,eAAe,OAAO;AAAA,UAC1C,OAAO,EAAE,IAAI;AAAA,UACb,QAAQ,EAAE,OAAO,GAAG,WAAW,MAAM,SAAS;AAAA,UAC9C,QAAQ,EAAE,KAAK,OAAO,GAAG,WAAW,MAAM,SAAS;AAAA,QACvD,CAAC;AACD,eAAO,EAAE,OAAO,UAAU,OAAO,WAAW,UAAU,UAAU;AAAA,MACpE,OAAO;AACH,cAAM,UAAU,MAAM,eAAe,OAAO;AAAA,UACxC,OAAO,EAAE,IAAI;AAAA,UACb,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE;AAAA,QACpC,CAAC;AACD,eAAO,EAAE,OAAO,QAAQ,OAAO,WAAW,QAAQ,UAAU;AAAA,MAChE;AAAA,IACF;AAEA,YAAQ,eAAe,OAAO,QAAgB;AAC5C,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,MAAM,eAAe,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACjE,UAAI,CAAC,UAAU,MAAM,OAAO,UAAW,QAAO;AAC9C,aAAO,EAAE,OAAO,OAAO,OAAO,WAAW,OAAO,UAAU;AAAA,IAC5D;AAAA,EACF;AAEA,SAAO;AACT;",
|
|
4
|
+
"sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates a Prisma adapter using a Prisma delegate (e.g., prisma.user).\r\n * \r\n * Works with any Prisma-supported database by using the standard\r\n * Prisma delegate operations (findUnique, create, update).\r\n * \r\n * @param model - A Prisma delegate instance (e.g., prisma.user).\r\n * @param rateLimitModel - An optional Prisma delegate for rate limit tracking.\r\n * @returns An AuthAdapter compliant object.\r\n */\r\nexport function createPrismaAdapter<TUser extends User = User>(model: any, rateLimitModel?: any): AuthAdapter<TUser> {\r\n const adapter: AuthAdapter<TUser> = {\r\n async createUser(data: any) {\r\n const dataToSave = { id: data.id || globalThis.crypto.randomUUID(), ...data };\r\n const user = await model.create({ data: dataToSave });\r\n return user as TUser;\r\n },\r\n\r\n async findUserByEmail(email: string) {\r\n const user = await model.findUnique({\r\n where: { email },\r\n });\r\n return user as TUser | null;\r\n },\r\n\r\n async findUserById(id: string) {\r\n const user = await model.findUnique({\r\n where: { id },\r\n });\r\n return user as TUser | null;\r\n },\r\n\r\n async updateUser(id: string, data: Partial<TUser>) {\r\n const user = await model.update({\r\n where: { id },\r\n data,\r\n });\r\n return user as TUser | null;\r\n },\r\n\r\n async linkOAuthAccount(userId: string, provider: string, providerId: string) {\r\n await model.update({\r\n where: { id: userId },\r\n data: {\r\n oauthProvider: provider,\r\n oauthId: providerId,\r\n },\r\n });\r\n },\r\n\r\n async invalidateSession(id: string) {\r\n await model.update({\r\n where: { id },\r\n data: {\r\n sessionVersion: { increment: 1 },\r\n },\r\n });\r\n },\r\n };\r\n\r\n if (rateLimitModel) {\r\n adapter.incrementRateLimit = async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n const record = await rateLimitModel.findUnique({ where: { key } });\r\n \r\n if (!record || now > record.resetTime) {\r\n const newRecord = await rateLimitModel.upsert({\r\n where: { key },\r\n update: { count: 1, resetTime: now + windowMs },\r\n create: { key, count: 1, resetTime: now + windowMs }\r\n });\r\n return { count: newRecord.count, resetTime: newRecord.resetTime };\r\n } else {\r\n const updated = await rateLimitModel.update({\r\n where: { key },\r\n data: { count: { increment: 1 } }\r\n });\r\n return { count: updated.count, resetTime: updated.resetTime };\r\n }\r\n };\r\n\r\n adapter.getRateLimit = async (key: string) => {\r\n const now = Date.now();\r\n const record = await rateLimitModel.findUnique({ where: { key } });\r\n if (!record || now > record.resetTime) return null;\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n }\r\n\r\n return adapter;\r\n}\r\n"],
|
|
5
|
+
"mappings": "AAYO,SAAS,oBAA+C,OAAY,gBAA0C;AACnH,QAAM,UAA8B;AAAA,IAClC,MAAM,WAAW,MAAW;AAC1B,YAAM,aAAa,EAAE,IAAI,KAAK,MAAM,WAAW,OAAO,WAAW,GAAG,GAAG,KAAK;AAC5E,YAAM,OAAO,MAAM,MAAM,OAAO,EAAE,MAAM,WAAW,CAAC;AACpD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,gBAAgB,OAAe;AACnC,YAAM,OAAO,MAAM,MAAM,WAAW;AAAA,QAClC,OAAO,EAAE,MAAM;AAAA,MACjB,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,aAAa,IAAY;AAC7B,YAAM,OAAO,MAAM,MAAM,WAAW;AAAA,QAClC,OAAO,EAAE,GAAG;AAAA,MACd,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,WAAW,IAAY,MAAsB;AACjD,YAAM,OAAO,MAAM,MAAM,OAAO;AAAA,QAC9B,OAAO,EAAE,GAAG;AAAA,QACZ;AAAA,MACF,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,iBAAiB,QAAgB,UAAkB,YAAoB;AAC3E,YAAM,MAAM,OAAO;AAAA,QACjB,OAAO,EAAE,IAAI,OAAO;AAAA,QACpB,MAAM;AAAA,UACJ,eAAe;AAAA,UACf,SAAS;AAAA,QACX;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,kBAAkB,IAAY;AAClC,YAAM,MAAM,OAAO;AAAA,QACjB,OAAO,EAAE,GAAG;AAAA,QACZ,MAAM;AAAA,UACJ,gBAAgB,EAAE,WAAW,EAAE;AAAA,QACjC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,gBAAgB;AAClB,YAAQ,qBAAqB,OAAO,KAAa,aAAqB;AACpE,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,MAAM,eAAe,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAEjE,UAAI,CAAC,UAAU,MAAM,OAAO,WAAW;AACnC,cAAM,YAAY,MAAM,eAAe,OAAO;AAAA,UAC1C,OAAO,EAAE,IAAI;AAAA,UACb,QAAQ,EAAE,OAAO,GAAG,WAAW,MAAM,SAAS;AAAA,UAC9C,QAAQ,EAAE,KAAK,OAAO,GAAG,WAAW,MAAM,SAAS;AAAA,QACvD,CAAC;AACD,eAAO,EAAE,OAAO,UAAU,OAAO,WAAW,UAAU,UAAU;AAAA,MACpE,OAAO;AACH,cAAM,UAAU,MAAM,eAAe,OAAO;AAAA,UACxC,OAAO,EAAE,IAAI;AAAA,UACb,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE;AAAA,QACpC,CAAC;AACD,eAAO,EAAE,OAAO,QAAQ,OAAO,WAAW,QAAQ,UAAU;AAAA,MAChE;AAAA,IACF;AAEA,YAAQ,eAAe,OAAO,QAAgB;AAC5C,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SAAS,MAAM,eAAe,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACjE,UAAI,CAAC,UAAU,MAAM,OAAO,UAAW,QAAO;AAC9C,aAAO,EAAE,OAAO,OAAO,OAAO,WAAW,OAAO,UAAU;AAAA,IAC5D;AAAA,EACF;AAEA,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/auth/core/index.cjs
CHANGED
|
@@ -43,7 +43,13 @@ function createAuth(options) {
|
|
|
43
43
|
const refreshExpiration = session?.refreshExpires || "7d";
|
|
44
44
|
const configuredRateLimiter = (0, import_rate_limit.createRateLimiter)(adapter, rateLimit);
|
|
45
45
|
async function generateToken(user, type = "access") {
|
|
46
|
-
let payload = {
|
|
46
|
+
let payload = {
|
|
47
|
+
sub: user.id,
|
|
48
|
+
role: user.role,
|
|
49
|
+
sv: user.sessionVersion || 0,
|
|
50
|
+
// Include Session Version
|
|
51
|
+
type
|
|
52
|
+
};
|
|
47
53
|
if (user.passwordHash && (type === "refresh" || session?.enforceStrictRevocation)) {
|
|
48
54
|
payload.pw_frag = user.passwordHash.slice(-10);
|
|
49
55
|
}
|
|
@@ -57,11 +63,15 @@ function createAuth(options) {
|
|
|
57
63
|
try {
|
|
58
64
|
const { payload } = await (0, import_jose.jwtVerify)(token, encodedSecret);
|
|
59
65
|
if (payload.type !== expectedType) return null;
|
|
60
|
-
if (expectedType === "access" && session?.enforceStrictRevocation
|
|
66
|
+
if (expectedType === "access" && session?.enforceStrictRevocation) {
|
|
61
67
|
const user = await adapter.findUserById(payload.sub);
|
|
62
|
-
if (!user
|
|
68
|
+
if (!user) return null;
|
|
69
|
+
if (payload.pw_frag && payload.pw_frag !== user.passwordHash?.slice(-10)) {
|
|
63
70
|
throw new Error("Strict Revocation: Access Token revoked due to password change");
|
|
64
71
|
}
|
|
72
|
+
if (payload.sv !== (user.sessionVersion || 0)) {
|
|
73
|
+
throw new Error("Strict Revocation: Session revoked (Logged out)");
|
|
74
|
+
}
|
|
65
75
|
}
|
|
66
76
|
return payload;
|
|
67
77
|
} catch (e) {
|
|
@@ -77,10 +87,13 @@ function createAuth(options) {
|
|
|
77
87
|
if (!user) {
|
|
78
88
|
throw new Error("User not found");
|
|
79
89
|
}
|
|
80
|
-
if (
|
|
81
|
-
if (payload.pw_frag !== user.passwordHash.slice(-10)) {
|
|
90
|
+
if (user.passwordHash) {
|
|
91
|
+
if (payload.pw_frag && payload.pw_frag !== user.passwordHash.slice(-10)) {
|
|
82
92
|
throw new Error("Session revoked (Password changed)");
|
|
83
93
|
}
|
|
94
|
+
if (payload.sv !== (user.sessionVersion || 0)) {
|
|
95
|
+
throw new Error("Session revoked (Logged out)");
|
|
96
|
+
}
|
|
84
97
|
}
|
|
85
98
|
const accessToken = await generateToken(user, "access");
|
|
86
99
|
return { accessToken };
|
|
@@ -157,10 +170,24 @@ function createAuth(options) {
|
|
|
157
170
|
}
|
|
158
171
|
return updatedUser;
|
|
159
172
|
}
|
|
173
|
+
async function logout(userId) {
|
|
174
|
+
if (!adapter.invalidateSession) {
|
|
175
|
+
if (adapter.updateUser) {
|
|
176
|
+
const user = await adapter.findUserById(userId);
|
|
177
|
+
const currentVersion = user?.sessionVersion || 0;
|
|
178
|
+
await adapter.updateUser(userId, { sessionVersion: currentVersion + 1 });
|
|
179
|
+
return { success: true };
|
|
180
|
+
}
|
|
181
|
+
throw new Error("Logout failed: The AuthAdapter does not support session invalidation.");
|
|
182
|
+
}
|
|
183
|
+
await adapter.invalidateSession(userId);
|
|
184
|
+
return { success: true };
|
|
185
|
+
}
|
|
160
186
|
return {
|
|
161
187
|
signup,
|
|
162
188
|
loginWithPassword,
|
|
163
189
|
changePassword,
|
|
190
|
+
logout,
|
|
164
191
|
refresh,
|
|
165
192
|
verifyToken,
|
|
166
193
|
generateToken,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/auth/core/index.ts"],
|
|
4
|
-
"sourcesContent": ["import * as argon2 from \"argon2\";\r\nimport { SignJWT, jwtVerify } from \"jose\";\r\nimport crypto from \"crypto\";\r\nimport type { AuthAdapter, User } from \"../adapters/index.js\";\r\nimport type { Provider } from \"../providers/index.js\";\r\nimport { createRateLimiter, type RateLimitOptions } from \"../security/rate-limit.js\";\r\n\r\nexport interface CreateAuthOptions {\r\n adapter: AuthAdapter<any>;\r\n secret: string | Uint8Array;\r\n pepper?: string;\r\n session?: {\r\n expires?: string | number; // For access tokens\r\n refreshExpires?: string | number; // For refresh tokens\r\n enforceStrictRevocation?: boolean; // If true, DB check on access tokens\r\n };\r\n providers?: Provider[];\r\n jwt?: {\r\n /**\r\n * A callback to add custom fields to the JWT payload.\r\n * It receives the user object and the token type ('access' or 'refresh').\r\n * Return an object containing the fields to be merged into the payload.\r\n * You can also override default fields like 'sub'.\r\n */\r\n payload?: (user: User<any>, type: \"access\" | \"refresh\") => Record<string, any>;\r\n };\r\n rateLimit?: RateLimitOptions;\r\n ipBlocking?: { maxStrikes: number; blockDurationMs: number };\r\n passwordPolicy?: {\r\n minLength?: number;\r\n requireUppercase?: boolean;\r\n requireLowercase?: boolean;\r\n requireNumber?: boolean;\r\n requireSpecialCharacter?: boolean;\r\n };\r\n}\r\n\r\nexport function createAuth(options: CreateAuthOptions) {\r\n const { adapter, secret, pepper, session, providers, rateLimit, ipBlocking } = options;\r\n const encodedSecret = typeof secret === \"string\" ? new TextEncoder().encode(secret) : secret;\r\n const expiration = session?.expires || \"1h\"; // Default access token to 1h\r\n const refreshExpiration = session?.refreshExpires || \"7d\";\r\n\r\n const configuredRateLimiter = createRateLimiter(adapter, rateLimit);\r\n\r\n /**\r\n * Generates a stateless JWT for a user session\r\n */\r\n async function generateToken(user: User<any>, type: \"access\" | \"refresh\" = \"access\") {\r\n let payload: Record<string, any> = { sub: user.id, role: user.role, type };\r\n\r\n // Lightweight Session Revocation: Link token to current password hash state\r\n if (user.passwordHash && (type === \"refresh\" || session?.enforceStrictRevocation)) {\r\n payload.pw_frag = user.passwordHash.slice(-10);\r\n }\r\n\r\n if (options.jwt?.payload) {\r\n const customPayload = options.jwt.payload(user, type);\r\n payload = { ...payload, ...customPayload };\r\n }\r\n\r\n return new SignJWT(payload)\r\n .setProtectedHeader({ alg: \"HS256\" })\r\n .setIssuedAt()\r\n .setExpirationTime(type === \"access\" ? expiration : refreshExpiration)\r\n .sign(encodedSecret);\r\n }\r\n\r\n /**\r\n * Verifies a JWT and returns the payload.\r\n * Optionally checks for a specific token type (access/refresh).\r\n */\r\n async function verifyToken(token: string, expectedType: \"access\" | \"refresh\" = \"access\") {\r\n try {\r\n const { payload } = await jwtVerify(token, encodedSecret);\r\n if (payload.type !== expectedType) return null;\r\n\r\n if (expectedType === \"access\" && session?.enforceStrictRevocation && payload.pw_frag) {\r\n const user = await adapter.findUserById(payload.sub as string);\r\n if (!user || payload.pw_frag !== user.passwordHash?.slice(-10)) {\r\n throw new Error(\"Strict Revocation: Access Token revoked due to password change\");\r\n }\r\n }\r\n\r\n return payload;\r\n } catch (e) {\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Refreshes an access token using a valid refresh token.\r\n */\r\n async function refresh(refreshToken: string) {\r\n const payload = await verifyToken(refreshToken, \"refresh\");\r\n if (!payload || !payload.sub) {\r\n throw new Error(\"Invalid or expired refresh token\");\r\n }\r\n\r\n const user = await adapter.findUserById(payload.sub as string);\r\n if (!user) {\r\n throw new Error(\"User not found\");\r\n }\r\n\r\n // Lightweight Session Revocation: Validate password hasn't changed since token issuance\r\n if (payload.pw_frag && user.passwordHash) {\r\n if (payload.pw_frag !== user.passwordHash.slice(-10)) {\r\n throw new Error(\"Session revoked (Password changed)\");\r\n }\r\n }\r\n\r\n const accessToken = await generateToken(user, \"access\");\r\n return { accessToken };\r\n }\r\n\r\n function validatePassword(password?: string) {\r\n if (!password || !options.passwordPolicy) return;\r\n \r\n const p = options.passwordPolicy;\r\n if (p.minLength && password.length < p.minLength) {\r\n throw new Error(`Password must be at least ${p.minLength} characters`);\r\n }\r\n if (p.requireUppercase && !/[A-Z]/.test(password)) {\r\n throw new Error(\"Password must contain at least one uppercase letter\");\r\n }\r\n if (p.requireLowercase && !/[a-z]/.test(password)) {\r\n throw new Error(\"Password must contain at least one lowercase letter\");\r\n }\r\n if (p.requireNumber && !/[0-9]/.test(password)) {\r\n throw new Error(\"Password must contain at least one number\");\r\n }\r\n if (p.requireSpecialCharacter && !/[^A-Za-z0-9]/.test(password)) {\r\n throw new Error(\"Password must contain at least one special character\");\r\n }\r\n }\r\n\r\n /**\r\n * Signup with a new user payload.\r\n * Incorporates server-side pepper for password hashing if provided.\r\n */\r\n async function signup(userData: Omit<User<any>, \"id\">, password?: string) {\r\n let dataToSave = { ...userData };\r\n\r\n if (password) {\r\n validatePassword(password);\r\n const passwordWithPepper = pepper ? `${password}${pepper}` : password;\r\n dataToSave.passwordHash = await argon2.hash(passwordWithPepper);\r\n }\r\n\r\n const newUser = await adapter.createUser(dataToSave);\r\n const accessToken = await generateToken(newUser, \"access\");\r\n const refreshToken = await generateToken(newUser, \"refresh\");\r\n\r\n return { user: newUser, accessToken, refreshToken };\r\n }\r\n\r\n /**\r\n * Standard Email/Password Login.\r\n * Includes timing attack protection and password peppering.\r\n */\r\n async function loginWithPassword(email: string, password: string, clientIp?: string) {\r\n if (ipBlocking && clientIp && configuredRateLimiter) {\r\n const strikeCheck = await configuredRateLimiter.check(`strike_${clientIp}`);\r\n if (strikeCheck && strikeCheck.count >= ipBlocking.maxStrikes) {\r\n throw new Error(\"IP is temporarily blocked.\");\r\n }\r\n }\r\n\r\n if (configuredRateLimiter) {\r\n const limitStatus = await configuredRateLimiter.increment(`login_${email}`);\r\n if (!limitStatus.success) {\r\n if (ipBlocking && clientIp) {\r\n await configuredRateLimiter.increment(`strike_${clientIp}`, ipBlocking.blockDurationMs);\r\n }\r\n throw new Error(\"Too many requests, please try again later.\");\r\n }\r\n }\r\n\r\n const user = await adapter.findUserByEmail(email);\r\n\r\n // Timing attack protection: Always verify a hash, even if user doesn't exist.\r\n // We use a dummy hash to keep execution time consistent.\r\n const dummyHash = \"$argon2id$v=19$m=65536,t=3,p=4$c29tZXNhbHQ$RytpInY7i6C9M5l0D4n8Q+7j/J+i\";\r\n const targetHash = user?.passwordHash || dummyHash;\r\n const passwordWithPepper = pepper ? `${password}${pepper}` : password;\r\n\r\n const isValid = await argon2.verify(targetHash, passwordWithPepper);\r\n\r\n if (!user || !user.passwordHash || !isValid) {\r\n throw new Error(\"Invalid credentials\");\r\n }\r\n\r\n const accessToken = await generateToken(user, \"access\");\r\n const refreshToken = await generateToken(user, \"refresh\");\r\n\r\n return { user, accessToken, refreshToken };\r\n }\r\n\r\n /**\r\n * Changes a user's password securely using the configured pepper and hashing algorithm.\r\n * Instantly revokes all active refresh tokens for the user globally.\r\n */\r\n async function changePassword(userId: string, newPassword: string) {\r\n if (!adapter.updateUser) {\r\n throw new Error(\"The AuthAdapter does not support updating user records natively.\");\r\n }\r\n\r\n validatePassword(newPassword);\r\n const passwordWithPepper = pepper ? `${newPassword}${pepper}` : newPassword;\r\n const newHash = await argon2.hash(passwordWithPepper);\r\n\r\n const updatedUser = await adapter.updateUser(userId, { passwordHash: newHash } as any);\r\n if (!updatedUser) {\r\n throw new Error(\"User not found\");\r\n }\r\n \r\n return updatedUser;\r\n }\r\n\r\n return {\r\n signup,\r\n loginWithPassword,\r\n changePassword,\r\n refresh,\r\n verifyToken,\r\n generateToken,\r\n _providers: providers\r\n };\r\n}\r\n\r\n/**\r\n * Utility to generate a high-entropy cryptographically secure secret.\r\n * Useful for initializing the 'secret' option in createAuth.\r\n */\r\nexport function generateSecret(length: number = 32): Uint8Array {\r\n return crypto.getRandomValues(new Uint8Array(length));\r\n}\r\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAAwB;AACxB,kBAAmC;AACnC,oBAAmB;AAGnB,wBAAyD;AAgClD,SAAS,WAAW,SAA4B;AACnD,QAAM,EAAE,SAAS,QAAQ,QAAQ,SAAS,WAAW,WAAW,WAAW,IAAI;AAC/E,QAAM,gBAAgB,OAAO,WAAW,WAAW,IAAI,YAAY,EAAE,OAAO,MAAM,IAAI;AACtF,QAAM,aAAa,SAAS,WAAW;AACvC,QAAM,oBAAoB,SAAS,kBAAkB;AAErD,QAAM,4BAAwB,qCAAkB,SAAS,SAAS;AAKlE,iBAAe,cAAc,MAAiB,OAA6B,UAAU;AACjF,QAAI,UAA+B,
|
|
4
|
+
"sourcesContent": ["import * as argon2 from \"argon2\";\r\nimport { SignJWT, jwtVerify } from \"jose\";\r\nimport crypto from \"crypto\";\r\nimport type { AuthAdapter, User } from \"../adapters/index.js\";\r\nimport type { Provider } from \"../providers/index.js\";\r\nimport { createRateLimiter, type RateLimitOptions } from \"../security/rate-limit.js\";\r\n\r\nexport interface CreateAuthOptions {\r\n adapter: AuthAdapter<any>;\r\n secret: string | Uint8Array;\r\n pepper?: string;\r\n session?: {\r\n expires?: string | number; // For access tokens\r\n refreshExpires?: string | number; // For refresh tokens\r\n enforceStrictRevocation?: boolean; // If true, DB check on access tokens\r\n };\r\n providers?: Provider[];\r\n jwt?: {\r\n /**\r\n * A callback to add custom fields to the JWT payload.\r\n * It receives the user object and the token type ('access' or 'refresh').\r\n * Return an object containing the fields to be merged into the payload.\r\n * You can also override default fields like 'sub'.\r\n */\r\n payload?: (user: User<any>, type: \"access\" | \"refresh\") => Record<string, any>;\r\n };\r\n rateLimit?: RateLimitOptions;\r\n ipBlocking?: { maxStrikes: number; blockDurationMs: number };\r\n passwordPolicy?: {\r\n minLength?: number;\r\n requireUppercase?: boolean;\r\n requireLowercase?: boolean;\r\n requireNumber?: boolean;\r\n requireSpecialCharacter?: boolean;\r\n };\r\n}\r\n\r\nexport function createAuth(options: CreateAuthOptions) {\r\n const { adapter, secret, pepper, session, providers, rateLimit, ipBlocking } = options;\r\n const encodedSecret = typeof secret === \"string\" ? new TextEncoder().encode(secret) : secret;\r\n const expiration = session?.expires || \"1h\"; // Default access token to 1h\r\n const refreshExpiration = session?.refreshExpires || \"7d\";\r\n\r\n const configuredRateLimiter = createRateLimiter(adapter, rateLimit);\r\n\r\n /**\r\n * Generates a stateless JWT for a user session\r\n */\r\n async function generateToken(user: User<any>, type: \"access\" | \"refresh\" = \"access\") {\r\n let payload: Record<string, any> = { \r\n sub: user.id, \r\n role: user.role, \r\n sv: user.sessionVersion || 0, // Include Session Version\r\n type \r\n };\r\n\r\n // Lightweight Session Revocation: Link token to current password hash state\r\n if (user.passwordHash && (type === \"refresh\" || session?.enforceStrictRevocation)) {\r\n payload.pw_frag = user.passwordHash.slice(-10);\r\n }\r\n\r\n if (options.jwt?.payload) {\r\n const customPayload = options.jwt.payload(user, type);\r\n payload = { ...payload, ...customPayload };\r\n }\r\n\r\n return new SignJWT(payload)\r\n .setProtectedHeader({ alg: \"HS256\" })\r\n .setIssuedAt()\r\n .setExpirationTime(type === \"access\" ? expiration : refreshExpiration)\r\n .sign(encodedSecret);\r\n }\r\n\r\n /**\r\n * Verifies a JWT and returns the payload.\r\n * Optionally checks for a specific token type (access/refresh).\r\n */\r\n async function verifyToken(token: string, expectedType: \"access\" | \"refresh\" = \"access\") {\r\n try {\r\n const { payload } = await jwtVerify(token, encodedSecret);\r\n if (payload.type !== expectedType) return null;\r\n\r\n if (expectedType === \"access\" && session?.enforceStrictRevocation) {\r\n const user = await adapter.findUserById(payload.sub as string);\r\n if (!user) return null;\r\n\r\n // Password change check\r\n if (payload.pw_frag && payload.pw_frag !== user.passwordHash?.slice(-10)) {\r\n throw new Error(\"Strict Revocation: Access Token revoked due to password change\");\r\n }\r\n\r\n // Global Logout (Session Version) check\r\n if (payload.sv !== (user.sessionVersion || 0)) {\r\n throw new Error(\"Strict Revocation: Session revoked (Logged out)\");\r\n }\r\n }\r\n\r\n return payload;\r\n } catch (e) {\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Refreshes an access token using a valid refresh token.\r\n */\r\n async function refresh(refreshToken: string) {\r\n const payload = await verifyToken(refreshToken, \"refresh\");\r\n if (!payload || !payload.sub) {\r\n throw new Error(\"Invalid or expired refresh token\");\r\n }\r\n\r\n const user = await adapter.findUserById(payload.sub as string);\r\n if (!user) {\r\n throw new Error(\"User not found\");\r\n }\r\n\r\n // Lightweight Session Revocation (Password change & Session Version)\r\n if (user.passwordHash) {\r\n if (payload.pw_frag && payload.pw_frag !== user.passwordHash.slice(-10)) {\r\n throw new Error(\"Session revoked (Password changed)\");\r\n }\r\n if (payload.sv !== (user.sessionVersion || 0)) {\r\n throw new Error(\"Session revoked (Logged out)\");\r\n }\r\n }\r\n\r\n const accessToken = await generateToken(user, \"access\");\r\n return { accessToken };\r\n }\r\n\r\n function validatePassword(password?: string) {\r\n if (!password || !options.passwordPolicy) return;\r\n \r\n const p = options.passwordPolicy;\r\n if (p.minLength && password.length < p.minLength) {\r\n throw new Error(`Password must be at least ${p.minLength} characters`);\r\n }\r\n if (p.requireUppercase && !/[A-Z]/.test(password)) {\r\n throw new Error(\"Password must contain at least one uppercase letter\");\r\n }\r\n if (p.requireLowercase && !/[a-z]/.test(password)) {\r\n throw new Error(\"Password must contain at least one lowercase letter\");\r\n }\r\n if (p.requireNumber && !/[0-9]/.test(password)) {\r\n throw new Error(\"Password must contain at least one number\");\r\n }\r\n if (p.requireSpecialCharacter && !/[^A-Za-z0-9]/.test(password)) {\r\n throw new Error(\"Password must contain at least one special character\");\r\n }\r\n }\r\n\r\n /**\r\n * Signup with a new user payload.\r\n * Incorporates server-side pepper for password hashing if provided.\r\n */\r\n async function signup(userData: Omit<User<any>, \"id\">, password?: string) {\r\n let dataToSave = { ...userData };\r\n\r\n if (password) {\r\n validatePassword(password);\r\n const passwordWithPepper = pepper ? `${password}${pepper}` : password;\r\n dataToSave.passwordHash = await argon2.hash(passwordWithPepper);\r\n }\r\n\r\n const newUser = await adapter.createUser(dataToSave);\r\n const accessToken = await generateToken(newUser, \"access\");\r\n const refreshToken = await generateToken(newUser, \"refresh\");\r\n\r\n return { user: newUser, accessToken, refreshToken };\r\n }\r\n\r\n /**\r\n * Standard Email/Password Login.\r\n * Includes timing attack protection and password peppering.\r\n */\r\n async function loginWithPassword(email: string, password: string, clientIp?: string) {\r\n if (ipBlocking && clientIp && configuredRateLimiter) {\r\n const strikeCheck = await configuredRateLimiter.check(`strike_${clientIp}`);\r\n if (strikeCheck && strikeCheck.count >= ipBlocking.maxStrikes) {\r\n throw new Error(\"IP is temporarily blocked.\");\r\n }\r\n }\r\n\r\n if (configuredRateLimiter) {\r\n const limitStatus = await configuredRateLimiter.increment(`login_${email}`);\r\n if (!limitStatus.success) {\r\n if (ipBlocking && clientIp) {\r\n await configuredRateLimiter.increment(`strike_${clientIp}`, ipBlocking.blockDurationMs);\r\n }\r\n throw new Error(\"Too many requests, please try again later.\");\r\n }\r\n }\r\n\r\n const user = await adapter.findUserByEmail(email);\r\n\r\n // Timing attack protection: Always verify a hash, even if user doesn't exist.\r\n // We use a dummy hash to keep execution time consistent.\r\n const dummyHash = \"$argon2id$v=19$m=65536,t=3,p=4$c29tZXNhbHQ$RytpInY7i6C9M5l0D4n8Q+7j/J+i\";\r\n const targetHash = user?.passwordHash || dummyHash;\r\n const passwordWithPepper = pepper ? `${password}${pepper}` : password;\r\n\r\n const isValid = await argon2.verify(targetHash, passwordWithPepper);\r\n\r\n if (!user || !user.passwordHash || !isValid) {\r\n throw new Error(\"Invalid credentials\");\r\n }\r\n\r\n const accessToken = await generateToken(user, \"access\");\r\n const refreshToken = await generateToken(user, \"refresh\");\r\n\r\n return { user, accessToken, refreshToken };\r\n }\r\n\r\n /**\r\n * Changes a user's password securely using the configured pepper and hashing algorithm.\r\n * Instantly revokes all active refresh tokens for the user globally.\r\n */\r\n async function changePassword(userId: string, newPassword: string) {\r\n if (!adapter.updateUser) {\r\n throw new Error(\"The AuthAdapter does not support updating user records natively.\");\r\n }\r\n\r\n validatePassword(newPassword);\r\n const passwordWithPepper = pepper ? `${newPassword}${pepper}` : newPassword;\r\n const newHash = await argon2.hash(passwordWithPepper);\r\n\r\n const updatedUser = await adapter.updateUser(userId, { passwordHash: newHash } as any);\r\n if (!updatedUser) {\r\n throw new Error(\"User not found\");\r\n }\r\n \r\n return updatedUser;\r\n }\r\n\r\n /**\r\n * Terminate all active sessions for a user globally.\r\n * This increments the session version in the database, invalidating all current tokens.\r\n */\r\n async function logout(userId: string) {\r\n if (!adapter.invalidateSession) {\r\n // If the adapter doesn't natively support it, we fall back to manual updateUser\r\n if (adapter.updateUser) {\r\n const user = await adapter.findUserById(userId);\r\n const currentVersion = user?.sessionVersion || 0;\r\n await adapter.updateUser(userId, { sessionVersion: currentVersion + 1 } as any);\r\n return { success: true };\r\n }\r\n throw new Error(\"Logout failed: The AuthAdapter does not support session invalidation.\");\r\n }\r\n await adapter.invalidateSession(userId);\r\n return { success: true };\r\n }\r\n\r\n return {\r\n signup,\r\n loginWithPassword,\r\n changePassword,\r\n logout,\r\n refresh,\r\n verifyToken,\r\n generateToken,\r\n _providers: providers\r\n };\r\n}\r\n\r\n/**\r\n * Utility to generate a high-entropy cryptographically secure secret.\r\n * Useful for initializing the 'secret' option in createAuth.\r\n */\r\nexport function generateSecret(length: number = 32): Uint8Array {\r\n return crypto.getRandomValues(new Uint8Array(length));\r\n}\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["crypto"]
|
|
7
7
|
}
|
|
@@ -45,6 +45,9 @@ export declare function createAuth(options: CreateAuthOptions): {
|
|
|
45
45
|
refreshToken: string;
|
|
46
46
|
}>;
|
|
47
47
|
changePassword: (userId: string, newPassword: string) => Promise<any>;
|
|
48
|
+
logout: (userId: string) => Promise<{
|
|
49
|
+
success: boolean;
|
|
50
|
+
}>;
|
|
48
51
|
refresh: (refreshToken: string) => Promise<{
|
|
49
52
|
accessToken: string;
|
|
50
53
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/core/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAErF,MAAM,WAAW,iBAAiB;IAC9B,OAAO,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAC1B,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QACjC,uBAAuB,CAAC,EAAE,OAAO,CAAC;KACrC,CAAC;IACF,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,GAAG,CAAC,EAAE;QACF;;;;;WAKG;QACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAClF,CAAC;IACF,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,UAAU,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,cAAc,CAAC,EAAE;QACb,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,uBAAuB,CAAC,EAAE,OAAO,CAAC;KACrC,CAAC;CACL;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/core/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAErF,MAAM,WAAW,iBAAiB;IAC9B,OAAO,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAC1B,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QACjC,uBAAuB,CAAC,EAAE,OAAO,CAAC;KACrC,CAAC;IACF,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,GAAG,CAAC,EAAE;QACF;;;;;WAKG;QACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAClF,CAAC;IACF,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,UAAU,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,cAAc,CAAC,EAAE;QACb,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,uBAAuB,CAAC,EAAE,OAAO,CAAC;KACrC,CAAC;CACL;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB;uBAuHjB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,aAAa,MAAM;;;;;+BAoBhC,MAAM,YAAY,MAAM,aAAa,MAAM;;;;;6BA0C7C,MAAM,eAAe,MAAM;qBAqBnC,MAAM;;;4BArIC,MAAM;;;yBA7BT,MAAM,iBAAgB,QAAQ,GAAG,SAAS;0BA7BzC,IAAI,CAAC,GAAG,CAAC,SAAQ,QAAQ,GAAG,SAAS;;EAwN3E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,GAAE,MAAW,GAAG,UAAU,CAE9D"}
|
package/dist/auth/core/index.js
CHANGED
|
@@ -9,7 +9,13 @@ function createAuth(options) {
|
|
|
9
9
|
const refreshExpiration = session?.refreshExpires || "7d";
|
|
10
10
|
const configuredRateLimiter = createRateLimiter(adapter, rateLimit);
|
|
11
11
|
async function generateToken(user, type = "access") {
|
|
12
|
-
let payload = {
|
|
12
|
+
let payload = {
|
|
13
|
+
sub: user.id,
|
|
14
|
+
role: user.role,
|
|
15
|
+
sv: user.sessionVersion || 0,
|
|
16
|
+
// Include Session Version
|
|
17
|
+
type
|
|
18
|
+
};
|
|
13
19
|
if (user.passwordHash && (type === "refresh" || session?.enforceStrictRevocation)) {
|
|
14
20
|
payload.pw_frag = user.passwordHash.slice(-10);
|
|
15
21
|
}
|
|
@@ -23,11 +29,15 @@ function createAuth(options) {
|
|
|
23
29
|
try {
|
|
24
30
|
const { payload } = await jwtVerify(token, encodedSecret);
|
|
25
31
|
if (payload.type !== expectedType) return null;
|
|
26
|
-
if (expectedType === "access" && session?.enforceStrictRevocation
|
|
32
|
+
if (expectedType === "access" && session?.enforceStrictRevocation) {
|
|
27
33
|
const user = await adapter.findUserById(payload.sub);
|
|
28
|
-
if (!user
|
|
34
|
+
if (!user) return null;
|
|
35
|
+
if (payload.pw_frag && payload.pw_frag !== user.passwordHash?.slice(-10)) {
|
|
29
36
|
throw new Error("Strict Revocation: Access Token revoked due to password change");
|
|
30
37
|
}
|
|
38
|
+
if (payload.sv !== (user.sessionVersion || 0)) {
|
|
39
|
+
throw new Error("Strict Revocation: Session revoked (Logged out)");
|
|
40
|
+
}
|
|
31
41
|
}
|
|
32
42
|
return payload;
|
|
33
43
|
} catch (e) {
|
|
@@ -43,10 +53,13 @@ function createAuth(options) {
|
|
|
43
53
|
if (!user) {
|
|
44
54
|
throw new Error("User not found");
|
|
45
55
|
}
|
|
46
|
-
if (
|
|
47
|
-
if (payload.pw_frag !== user.passwordHash.slice(-10)) {
|
|
56
|
+
if (user.passwordHash) {
|
|
57
|
+
if (payload.pw_frag && payload.pw_frag !== user.passwordHash.slice(-10)) {
|
|
48
58
|
throw new Error("Session revoked (Password changed)");
|
|
49
59
|
}
|
|
60
|
+
if (payload.sv !== (user.sessionVersion || 0)) {
|
|
61
|
+
throw new Error("Session revoked (Logged out)");
|
|
62
|
+
}
|
|
50
63
|
}
|
|
51
64
|
const accessToken = await generateToken(user, "access");
|
|
52
65
|
return { accessToken };
|
|
@@ -123,10 +136,24 @@ function createAuth(options) {
|
|
|
123
136
|
}
|
|
124
137
|
return updatedUser;
|
|
125
138
|
}
|
|
139
|
+
async function logout(userId) {
|
|
140
|
+
if (!adapter.invalidateSession) {
|
|
141
|
+
if (adapter.updateUser) {
|
|
142
|
+
const user = await adapter.findUserById(userId);
|
|
143
|
+
const currentVersion = user?.sessionVersion || 0;
|
|
144
|
+
await adapter.updateUser(userId, { sessionVersion: currentVersion + 1 });
|
|
145
|
+
return { success: true };
|
|
146
|
+
}
|
|
147
|
+
throw new Error("Logout failed: The AuthAdapter does not support session invalidation.");
|
|
148
|
+
}
|
|
149
|
+
await adapter.invalidateSession(userId);
|
|
150
|
+
return { success: true };
|
|
151
|
+
}
|
|
126
152
|
return {
|
|
127
153
|
signup,
|
|
128
154
|
loginWithPassword,
|
|
129
155
|
changePassword,
|
|
156
|
+
logout,
|
|
130
157
|
refresh,
|
|
131
158
|
verifyToken,
|
|
132
159
|
generateToken,
|