kroxt 1.2.1 → 1.3.0

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.
Files changed (91) hide show
  1. package/README.md +29 -96
  2. package/dist/adapters/drizzle.d.ts +2 -1
  3. package/dist/adapters/drizzle.d.ts.map +1 -1
  4. package/dist/adapters/index.d.ts +9 -0
  5. package/dist/adapters/index.d.ts.map +1 -1
  6. package/dist/adapters/memory.d.ts.map +1 -1
  7. package/dist/adapters/mongoose.d.ts +7 -1
  8. package/dist/adapters/mongoose.d.ts.map +1 -1
  9. package/dist/adapters/prisma.d.ts +2 -1
  10. package/dist/adapters/prisma.d.ts.map +1 -1
  11. package/dist/{adapters → auth/adapters}/drizzle.cjs +33 -2
  12. package/dist/auth/adapters/drizzle.cjs.map +7 -0
  13. package/dist/auth/adapters/drizzle.js +58 -0
  14. package/dist/auth/adapters/drizzle.js.map +7 -0
  15. package/dist/auth/adapters/index.cjs.map +7 -0
  16. package/dist/{adapters → auth/adapters}/memory.cjs +28 -0
  17. package/dist/auth/adapters/memory.cjs.map +7 -0
  18. package/dist/auth/adapters/memory.js +59 -0
  19. package/dist/auth/adapters/memory.js.map +7 -0
  20. package/dist/auth/adapters/mongoose.cjs +99 -0
  21. package/dist/auth/adapters/mongoose.cjs.map +7 -0
  22. package/dist/auth/adapters/mongoose.js +74 -0
  23. package/dist/auth/adapters/mongoose.js.map +7 -0
  24. package/dist/{adapters → auth/adapters}/prisma.cjs +36 -2
  25. package/dist/auth/adapters/prisma.cjs.map +7 -0
  26. package/dist/auth/adapters/prisma.js +68 -0
  27. package/dist/auth/adapters/prisma.js.map +7 -0
  28. package/dist/{core → auth/core}/index.cjs +67 -2
  29. package/dist/auth/core/index.cjs.map +7 -0
  30. package/dist/auth/core/index.js +143 -0
  31. package/dist/auth/core/index.js.map +7 -0
  32. package/dist/{index.cjs → auth/index.cjs} +12 -4
  33. package/dist/auth/index.cjs.map +7 -0
  34. package/dist/{index.js → auth/index.js} +6 -1
  35. package/dist/auth/index.js.map +7 -0
  36. package/dist/auth/providers/index.cjs.map +7 -0
  37. package/dist/auth/providers/index.js.map +7 -0
  38. package/dist/{security → auth/security}/index.cjs +10 -9
  39. package/dist/auth/security/index.cjs.map +7 -0
  40. package/dist/auth/security/index.js +19 -0
  41. package/dist/auth/security/index.js.map +7 -0
  42. package/dist/auth/security/rate-limit.cjs +82 -0
  43. package/dist/auth/security/rate-limit.cjs.map +7 -0
  44. package/dist/auth/security/rate-limit.js +57 -0
  45. package/dist/auth/security/rate-limit.js.map +7 -0
  46. package/dist/cli/index.cjs +134 -0
  47. package/dist/cli/index.cjs.map +7 -0
  48. package/dist/cli/index.js +111 -0
  49. package/dist/cli/index.js.map +7 -0
  50. package/dist/cli/templates.cjs +147 -0
  51. package/dist/cli/templates.cjs.map +7 -0
  52. package/dist/cli/templates.js +111 -0
  53. package/dist/cli/templates.js.map +7 -0
  54. package/dist/core/index.d.ts +16 -1
  55. package/dist/core/index.d.ts.map +1 -1
  56. package/dist/index.d.ts +3 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/security/index.d.ts +1 -0
  59. package/dist/security/index.d.ts.map +1 -1
  60. package/dist/security/rate-limit.d.ts +39 -0
  61. package/dist/security/rate-limit.d.ts.map +1 -0
  62. package/package.json +8 -2
  63. package/dist/adapters/drizzle.cjs.map +0 -7
  64. package/dist/adapters/drizzle.js +0 -27
  65. package/dist/adapters/drizzle.js.map +0 -7
  66. package/dist/adapters/index.cjs.map +0 -7
  67. package/dist/adapters/memory.cjs.map +0 -7
  68. package/dist/adapters/memory.js +0 -31
  69. package/dist/adapters/memory.js.map +0 -7
  70. package/dist/adapters/mongoose.cjs +0 -55
  71. package/dist/adapters/mongoose.cjs.map +0 -7
  72. package/dist/adapters/mongoose.js +0 -31
  73. package/dist/adapters/mongoose.js.map +0 -7
  74. package/dist/adapters/prisma.cjs.map +0 -7
  75. package/dist/adapters/prisma.js +0 -34
  76. package/dist/adapters/prisma.js.map +0 -7
  77. package/dist/core/index.cjs.map +0 -7
  78. package/dist/core/index.js +0 -78
  79. package/dist/core/index.js.map +0 -7
  80. package/dist/index.cjs.map +0 -7
  81. package/dist/index.js.map +0 -7
  82. package/dist/providers/index.cjs.map +0 -7
  83. package/dist/providers/index.js.map +0 -7
  84. package/dist/security/index.cjs.map +0 -7
  85. package/dist/security/index.js +0 -20
  86. package/dist/security/index.js.map +0 -7
  87. /package/dist/{adapters → auth/adapters}/index.cjs +0 -0
  88. /package/dist/{adapters → auth/adapters}/index.js +0 -0
  89. /package/dist/{adapters → auth/adapters}/index.js.map +0 -0
  90. /package/dist/{providers → auth/providers}/index.cjs +0 -0
  91. /package/dist/{providers → auth/providers}/index.js +0 -0
@@ -0,0 +1,143 @@
1
+ import * as argon2 from "argon2";
2
+ import { SignJWT, jwtVerify } from "jose";
3
+ import crypto from "crypto";
4
+ import { createRateLimiter } from "../security/rate-limit.js";
5
+ function createAuth(options) {
6
+ const { adapter, secret, pepper, session, providers, rateLimit, ipBlocking } = options;
7
+ const encodedSecret = typeof secret === "string" ? new TextEncoder().encode(secret) : secret;
8
+ const expiration = session?.expires || "1h";
9
+ const refreshExpiration = session?.refreshExpires || "7d";
10
+ const configuredRateLimiter = createRateLimiter(adapter, rateLimit);
11
+ async function generateToken(user, type = "access") {
12
+ let payload = { sub: user.id, role: user.role, type };
13
+ if (user.passwordHash && (type === "refresh" || session?.enforceStrictRevocation)) {
14
+ payload.pw_frag = user.passwordHash.slice(-10);
15
+ }
16
+ if (options.jwt?.payload) {
17
+ const customPayload = options.jwt.payload(user, type);
18
+ payload = { ...payload, ...customPayload };
19
+ }
20
+ return new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(type === "access" ? expiration : refreshExpiration).sign(encodedSecret);
21
+ }
22
+ async function verifyToken(token, expectedType = "access") {
23
+ try {
24
+ const { payload } = await jwtVerify(token, encodedSecret);
25
+ if (payload.type !== expectedType) return null;
26
+ if (expectedType === "access" && session?.enforceStrictRevocation && payload.pw_frag) {
27
+ const user = await adapter.findUserById(payload.sub);
28
+ if (!user || payload.pw_frag !== user.passwordHash?.slice(-10)) {
29
+ throw new Error("Strict Revocation: Access Token revoked due to password change");
30
+ }
31
+ }
32
+ return payload;
33
+ } catch (e) {
34
+ return null;
35
+ }
36
+ }
37
+ async function refresh(refreshToken) {
38
+ const payload = await verifyToken(refreshToken, "refresh");
39
+ if (!payload || !payload.sub) {
40
+ throw new Error("Invalid or expired refresh token");
41
+ }
42
+ const user = await adapter.findUserById(payload.sub);
43
+ if (!user) {
44
+ throw new Error("User not found");
45
+ }
46
+ if (payload.pw_frag && user.passwordHash) {
47
+ if (payload.pw_frag !== user.passwordHash.slice(-10)) {
48
+ throw new Error("Session revoked (Password changed)");
49
+ }
50
+ }
51
+ const accessToken = await generateToken(user, "access");
52
+ return { accessToken };
53
+ }
54
+ function validatePassword(password) {
55
+ if (!password || !options.passwordPolicy) return;
56
+ const p = options.passwordPolicy;
57
+ if (p.minLength && password.length < p.minLength) {
58
+ throw new Error(`Password must be at least ${p.minLength} characters`);
59
+ }
60
+ if (p.requireUppercase && !/[A-Z]/.test(password)) {
61
+ throw new Error("Password must contain at least one uppercase letter");
62
+ }
63
+ if (p.requireLowercase && !/[a-z]/.test(password)) {
64
+ throw new Error("Password must contain at least one lowercase letter");
65
+ }
66
+ if (p.requireNumber && !/[0-9]/.test(password)) {
67
+ throw new Error("Password must contain at least one number");
68
+ }
69
+ if (p.requireSpecialCharacter && !/[^A-Za-z0-9]/.test(password)) {
70
+ throw new Error("Password must contain at least one special character");
71
+ }
72
+ }
73
+ async function signup(userData, password) {
74
+ let dataToSave = { ...userData };
75
+ if (password) {
76
+ validatePassword(password);
77
+ const passwordWithPepper = pepper ? `${password}${pepper}` : password;
78
+ dataToSave.passwordHash = await argon2.hash(passwordWithPepper);
79
+ }
80
+ const newUser = await adapter.createUser(dataToSave);
81
+ const accessToken = await generateToken(newUser, "access");
82
+ const refreshToken = await generateToken(newUser, "refresh");
83
+ return { user: newUser, accessToken, refreshToken };
84
+ }
85
+ async function loginWithPassword(email, password, clientIp) {
86
+ if (ipBlocking && clientIp && configuredRateLimiter) {
87
+ const strikeCheck = await configuredRateLimiter.check(`strike_${clientIp}`);
88
+ if (strikeCheck && strikeCheck.count >= ipBlocking.maxStrikes) {
89
+ throw new Error("IP is temporarily blocked.");
90
+ }
91
+ }
92
+ if (configuredRateLimiter) {
93
+ const limitStatus = await configuredRateLimiter.increment(`login_${email}`);
94
+ if (!limitStatus.success) {
95
+ if (ipBlocking && clientIp) {
96
+ await configuredRateLimiter.increment(`strike_${clientIp}`, ipBlocking.blockDurationMs);
97
+ }
98
+ throw new Error("Too many requests, please try again later.");
99
+ }
100
+ }
101
+ const user = await adapter.findUserByEmail(email);
102
+ const dummyHash = "$argon2id$v=19$m=65536,t=3,p=4$c29tZXNhbHQ$RytpInY7i6C9M5l0D4n8Q+7j/J+i";
103
+ const targetHash = user?.passwordHash || dummyHash;
104
+ const passwordWithPepper = pepper ? `${password}${pepper}` : password;
105
+ const isValid = await argon2.verify(targetHash, passwordWithPepper);
106
+ if (!user || !user.passwordHash || !isValid) {
107
+ throw new Error("Invalid credentials");
108
+ }
109
+ const accessToken = await generateToken(user, "access");
110
+ const refreshToken = await generateToken(user, "refresh");
111
+ return { user, accessToken, refreshToken };
112
+ }
113
+ async function changePassword(userId, newPassword) {
114
+ if (!adapter.updateUser) {
115
+ throw new Error("The AuthAdapter does not support updating user records natively.");
116
+ }
117
+ validatePassword(newPassword);
118
+ const passwordWithPepper = pepper ? `${newPassword}${pepper}` : newPassword;
119
+ const newHash = await argon2.hash(passwordWithPepper);
120
+ const updatedUser = await adapter.updateUser(userId, { passwordHash: newHash });
121
+ if (!updatedUser) {
122
+ throw new Error("User not found");
123
+ }
124
+ return updatedUser;
125
+ }
126
+ return {
127
+ signup,
128
+ loginWithPassword,
129
+ changePassword,
130
+ refresh,
131
+ verifyToken,
132
+ generateToken,
133
+ _providers: providers
134
+ };
135
+ }
136
+ function generateSecret(length = 32) {
137
+ return crypto.getRandomValues(new Uint8Array(length));
138
+ }
139
+ export {
140
+ createAuth,
141
+ generateSecret
142
+ };
143
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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": "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",
6
+ "names": []
7
+ }
@@ -17,28 +17,36 @@ var __copyProps = (to, from, except, desc) => {
17
17
  };
18
18
  var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
19
19
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
- var index_exports = {};
21
- __export(index_exports, {
20
+ var auth_exports = {};
21
+ __export(auth_exports, {
22
22
  GitHub: () => import_providers.GitHub,
23
23
  Google: () => import_providers.Google,
24
24
  createAuth: () => import_core.createAuth,
25
+ createDrizzleAdapter: () => import_drizzle.createDrizzleAdapter,
25
26
  createMemoryAdapter: () => import_memory.createMemoryAdapter,
26
27
  createMongoAdapter: () => import_mongoose.createMongoAdapter,
28
+ createPrismaAdapter: () => import_prisma.createPrismaAdapter,
29
+ createRateLimitModel: () => import_mongoose.createRateLimitModel,
27
30
  generateSecret: () => import_core.generateSecret
28
31
  });
29
- module.exports = __toCommonJS(index_exports);
32
+ module.exports = __toCommonJS(auth_exports);
30
33
  var import_providers = require("./providers/index.js");
31
34
  var import_core = require("./core/index.js");
32
35
  var import_memory = require("./adapters/memory.js");
33
36
  var import_mongoose = require("./adapters/mongoose.js");
34
- __reExport(index_exports, require("./security/index.js"), module.exports);
37
+ var import_prisma = require("./adapters/prisma.js");
38
+ var import_drizzle = require("./adapters/drizzle.js");
39
+ __reExport(auth_exports, require("./security/index.js"), module.exports);
35
40
  // Annotate the CommonJS export names for ESM import in node:
36
41
  0 && (module.exports = {
37
42
  GitHub,
38
43
  Google,
39
44
  createAuth,
45
+ createDrizzleAdapter,
40
46
  createMemoryAdapter,
41
47
  createMongoAdapter,
48
+ createPrismaAdapter,
49
+ createRateLimitModel,
42
50
  generateSecret,
43
51
  ...require("./security/index.js")
44
52
  });
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/auth/index.ts"],
4
+ "sourcesContent": ["export type { AuthAdapter, User, BaseUser } from \"./adapters/index.js\";\r\nexport { GitHub, Google } from \"./providers/index.js\";\r\nexport type { Provider, ProviderConfig } from \"./providers/index.js\";\r\nexport { createAuth, generateSecret } from \"./core/index.js\";\r\nexport type { CreateAuthOptions } from \"./core/index.js\";\r\nexport { createMemoryAdapter } from \"./adapters/memory.js\";\r\nexport { createMongoAdapter, createRateLimitModel } from \"./adapters/mongoose.js\";\r\nexport { createPrismaAdapter } from \"./adapters/prisma.js\";\r\nexport { createDrizzleAdapter } from \"./adapters/drizzle.js\";\r\nexport * from \"./security/index.js\";\r\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,uBAA+B;AAE/B,kBAA2C;AAE3C,oBAAoC;AACpC,sBAAyD;AACzD,oBAAoC;AACpC,qBAAqC;AACrC,yBAAc,gCATd;",
6
+ "names": []
7
+ }
@@ -1,14 +1,19 @@
1
1
  import { GitHub, Google } from "./providers/index.js";
2
2
  import { createAuth, generateSecret } from "./core/index.js";
3
3
  import { createMemoryAdapter } from "./adapters/memory.js";
4
- import { createMongoAdapter } from "./adapters/mongoose.js";
4
+ import { createMongoAdapter, createRateLimitModel } from "./adapters/mongoose.js";
5
+ import { createPrismaAdapter } from "./adapters/prisma.js";
6
+ import { createDrizzleAdapter } from "./adapters/drizzle.js";
5
7
  export * from "./security/index.js";
6
8
  export {
7
9
  GitHub,
8
10
  Google,
9
11
  createAuth,
12
+ createDrizzleAdapter,
10
13
  createMemoryAdapter,
11
14
  createMongoAdapter,
15
+ createPrismaAdapter,
16
+ createRateLimitModel,
12
17
  generateSecret
13
18
  };
14
19
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/auth/index.ts"],
4
+ "sourcesContent": ["export type { AuthAdapter, User, BaseUser } from \"./adapters/index.js\";\r\nexport { GitHub, Google } from \"./providers/index.js\";\r\nexport type { Provider, ProviderConfig } from \"./providers/index.js\";\r\nexport { createAuth, generateSecret } from \"./core/index.js\";\r\nexport type { CreateAuthOptions } from \"./core/index.js\";\r\nexport { createMemoryAdapter } from \"./adapters/memory.js\";\r\nexport { createMongoAdapter, createRateLimitModel } from \"./adapters/mongoose.js\";\r\nexport { createPrismaAdapter } from \"./adapters/prisma.js\";\r\nexport { createDrizzleAdapter } from \"./adapters/drizzle.js\";\r\nexport * from \"./security/index.js\";\r\n"],
5
+ "mappings": "AACA,SAAS,QAAQ,cAAc;AAE/B,SAAS,YAAY,sBAAsB;AAE3C,SAAS,2BAA2B;AACpC,SAAS,oBAAoB,4BAA4B;AACzD,SAAS,2BAA2B;AACpC,SAAS,4BAA4B;AACrC,cAAc;",
6
+ "names": []
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/auth/providers/index.ts"],
4
+ "sourcesContent": ["import { GitHub as ArcticGitHub, Google as ArcticGoogle } from \"arctic\";\r\n\r\nexport interface ProviderConfig {\r\n clientId: string;\r\n clientSecret: string;\r\n redirectURI?: string;\r\n}\r\n\r\nexport interface Provider {\r\n id: string;\r\n handler: any; // `arctic` provider instance\r\n}\r\n\r\nexport function GitHub(config: ProviderConfig): Provider {\r\n return {\r\n id: \"github\",\r\n handler: new ArcticGitHub(config.clientId, config.clientSecret, null),\r\n };\r\n}\r\n\r\nexport function Google(config: ProviderConfig): Provider {\r\n if (!config.redirectURI) {\r\n throw new Error(\"redirectURI is required for Google OAuth provider\");\r\n }\r\n return {\r\n id: \"google\",\r\n handler: new ArcticGoogle(\r\n config.clientId,\r\n config.clientSecret,\r\n config.redirectURI\r\n ),\r\n };\r\n}\r\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA+D;AAaxD,SAAS,OAAO,QAAkC;AACrD,SAAO;AAAA,IACH,IAAI;AAAA,IACJ,SAAS,IAAI,cAAAA,OAAa,OAAO,UAAU,OAAO,cAAc,IAAI;AAAA,EACxE;AACJ;AAEO,SAAS,OAAO,QAAkC;AACrD,MAAI,CAAC,OAAO,aAAa;AACrB,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACvE;AACA,SAAO;AAAA,IACH,IAAI;AAAA,IACJ,SAAS,IAAI,cAAAC;AAAA,MACT,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,IACX;AAAA,EACJ;AACJ;",
6
+ "names": ["ArcticGitHub", "ArcticGoogle"]
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/auth/providers/index.ts"],
4
+ "sourcesContent": ["import { GitHub as ArcticGitHub, Google as ArcticGoogle } from \"arctic\";\r\n\r\nexport interface ProviderConfig {\r\n clientId: string;\r\n clientSecret: string;\r\n redirectURI?: string;\r\n}\r\n\r\nexport interface Provider {\r\n id: string;\r\n handler: any; // `arctic` provider instance\r\n}\r\n\r\nexport function GitHub(config: ProviderConfig): Provider {\r\n return {\r\n id: \"github\",\r\n handler: new ArcticGitHub(config.clientId, config.clientSecret, null),\r\n };\r\n}\r\n\r\nexport function Google(config: ProviderConfig): Provider {\r\n if (!config.redirectURI) {\r\n throw new Error(\"redirectURI is required for Google OAuth provider\");\r\n }\r\n return {\r\n id: \"google\",\r\n handler: new ArcticGoogle(\r\n config.clientId,\r\n config.clientSecret,\r\n config.redirectURI\r\n ),\r\n };\r\n}\r\n"],
5
+ "mappings": "AAAA,SAAS,UAAU,cAAc,UAAU,oBAAoB;AAaxD,SAAS,OAAO,QAAkC;AACrD,SAAO;AAAA,IACH,IAAI;AAAA,IACJ,SAAS,IAAI,aAAa,OAAO,UAAU,OAAO,cAAc,IAAI;AAAA,EACxE;AACJ;AAEO,SAAS,OAAO,QAAkC;AACrD,MAAI,CAAC,OAAO,aAAa;AACrB,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACvE;AACA,SAAO;AAAA,IACH,IAAI;AAAA,IACJ,SAAS,IAAI;AAAA,MACT,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,IACX;AAAA,EACJ;AACJ;",
6
+ "names": []
7
+ }
@@ -17,6 +17,7 @@ var __copyProps = (to, from, except, desc) => {
17
17
  }
18
18
  return to;
19
19
  };
20
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
20
21
  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
22
  // If the importer is in node compatibility mode or this is not an ESM
22
23
  // file that has been converted to a CommonJS file using a Babel-
@@ -33,23 +34,23 @@ __export(security_exports, {
33
34
  });
34
35
  module.exports = __toCommonJS(security_exports);
35
36
  var import_crypto = __toESM(require("crypto"), 1);
37
+ __reExport(security_exports, require("./rate-limit.js"), module.exports);
36
38
  function generateCsrfToken() {
37
39
  return import_crypto.default.randomBytes(32).toString("hex");
38
40
  }
39
41
  function verifyCsrf(tokenInRequest, tokenInCookie) {
40
42
  if (!tokenInRequest || !tokenInCookie) return false;
41
- try {
42
- return import_crypto.default.timingSafeEqual(
43
- Buffer.from(tokenInRequest),
44
- Buffer.from(tokenInCookie)
45
- );
46
- } catch {
47
- return false;
48
- }
43
+ const isValid = /^[a-f0-9]{64}$/i.test(tokenInRequest) && /^[a-f0-9]{64}$/i.test(tokenInCookie);
44
+ if (!isValid) return false;
45
+ const bufA = Buffer.from(tokenInRequest, "hex");
46
+ const bufB = Buffer.from(tokenInCookie, "hex");
47
+ if (bufA.length !== bufB.length) return false;
48
+ return import_crypto.default.timingSafeEqual(bufA, bufB);
49
49
  }
50
50
  // Annotate the CommonJS export names for ESM import in node:
51
51
  0 && (module.exports = {
52
52
  generateCsrfToken,
53
- verifyCsrf
53
+ verifyCsrf,
54
+ ...require("./rate-limit.js")
54
55
  });
55
56
  //# sourceMappingURL=index.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/auth/security/index.ts"],
4
+ "sourcesContent": ["import crypto from \"crypto\";\r\n\r\n/**\r\n * Generates a stateless CSRF token using the double-submit cookie pattern.\r\n * This is recommended for Express/Kroxt setups using cookies for sessions.\r\n */\r\nexport function generateCsrfToken(): string {\r\n return crypto.randomBytes(32).toString(\"hex\");\r\n}\r\n\r\n/**\r\n * Simple middleware-ready check for CSRF tokens.\r\n * Matches a token from the request body/headers against a cookie.\r\n */\r\nexport function verifyCsrf(tokenInRequest: string, tokenInCookie: string): boolean {\r\n if (!tokenInRequest || !tokenInCookie) return false;\r\n\r\n const isValid =\r\n /^[a-f0-9]{64}$/i.test(tokenInRequest) &&\r\n /^[a-f0-9]{64}$/i.test(tokenInCookie);\r\n\r\n if (!isValid) return false;\r\n\r\n const bufA = Buffer.from(tokenInRequest, \"hex\");\r\n const bufB = Buffer.from(tokenInCookie, \"hex\");\r\n\r\n if (bufA.length !== bufB.length) return false;\r\n\r\n return crypto.timingSafeEqual(bufA, bufB);\r\n}\r\n\r\n/**\r\n * Security Recommendations for Kroxt:\r\n * 1. Always set cookies with: httpOnly: true, secure: true, sameSite: 'strict'\r\n * 2. Use a 'pepper' in createAuth to protect hashes.\r\n * 3. Implement rate limiting on /login and /register endpoints.\r\n */\r\n\r\nexport * from \"./rate-limit.js\";\r\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAmB;AAsCnB,6BAAc,4BAtCd;AAMO,SAAS,oBAA4B;AACxC,SAAO,cAAAA,QAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AAChD;AAMO,SAAS,WAAW,gBAAwB,eAAgC;AAC/E,MAAI,CAAC,kBAAkB,CAAC,cAAe,QAAO;AAE9C,QAAM,UACF,kBAAkB,KAAK,cAAc,KACrC,kBAAkB,KAAK,aAAa;AAExC,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,OAAO,OAAO,KAAK,gBAAgB,KAAK;AAC9C,QAAM,OAAO,OAAO,KAAK,eAAe,KAAK;AAE7C,MAAI,KAAK,WAAW,KAAK,OAAQ,QAAO;AAExC,SAAO,cAAAA,QAAO,gBAAgB,MAAM,IAAI;AAC5C;",
6
+ "names": ["crypto"]
7
+ }
@@ -0,0 +1,19 @@
1
+ import crypto from "crypto";
2
+ function generateCsrfToken() {
3
+ return crypto.randomBytes(32).toString("hex");
4
+ }
5
+ function verifyCsrf(tokenInRequest, tokenInCookie) {
6
+ if (!tokenInRequest || !tokenInCookie) return false;
7
+ const isValid = /^[a-f0-9]{64}$/i.test(tokenInRequest) && /^[a-f0-9]{64}$/i.test(tokenInCookie);
8
+ if (!isValid) return false;
9
+ const bufA = Buffer.from(tokenInRequest, "hex");
10
+ const bufB = Buffer.from(tokenInCookie, "hex");
11
+ if (bufA.length !== bufB.length) return false;
12
+ return crypto.timingSafeEqual(bufA, bufB);
13
+ }
14
+ export * from "./rate-limit.js";
15
+ export {
16
+ generateCsrfToken,
17
+ verifyCsrf
18
+ };
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/auth/security/index.ts"],
4
+ "sourcesContent": ["import crypto from \"crypto\";\r\n\r\n/**\r\n * Generates a stateless CSRF token using the double-submit cookie pattern.\r\n * This is recommended for Express/Kroxt setups using cookies for sessions.\r\n */\r\nexport function generateCsrfToken(): string {\r\n return crypto.randomBytes(32).toString(\"hex\");\r\n}\r\n\r\n/**\r\n * Simple middleware-ready check for CSRF tokens.\r\n * Matches a token from the request body/headers against a cookie.\r\n */\r\nexport function verifyCsrf(tokenInRequest: string, tokenInCookie: string): boolean {\r\n if (!tokenInRequest || !tokenInCookie) return false;\r\n\r\n const isValid =\r\n /^[a-f0-9]{64}$/i.test(tokenInRequest) &&\r\n /^[a-f0-9]{64}$/i.test(tokenInCookie);\r\n\r\n if (!isValid) return false;\r\n\r\n const bufA = Buffer.from(tokenInRequest, \"hex\");\r\n const bufB = Buffer.from(tokenInCookie, \"hex\");\r\n\r\n if (bufA.length !== bufB.length) return false;\r\n\r\n return crypto.timingSafeEqual(bufA, bufB);\r\n}\r\n\r\n/**\r\n * Security Recommendations for Kroxt:\r\n * 1. Always set cookies with: httpOnly: true, secure: true, sameSite: 'strict'\r\n * 2. Use a 'pepper' in createAuth to protect hashes.\r\n * 3. Implement rate limiting on /login and /register endpoints.\r\n */\r\n\r\nexport * from \"./rate-limit.js\";\r\n"],
5
+ "mappings": "AAAA,OAAO,YAAY;AAMZ,SAAS,oBAA4B;AACxC,SAAO,OAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AAChD;AAMO,SAAS,WAAW,gBAAwB,eAAgC;AAC/E,MAAI,CAAC,kBAAkB,CAAC,cAAe,QAAO;AAE9C,QAAM,UACF,kBAAkB,KAAK,cAAc,KACrC,kBAAkB,KAAK,aAAa;AAExC,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,OAAO,OAAO,KAAK,gBAAgB,KAAK;AAC9C,QAAM,OAAO,OAAO,KAAK,eAAe,KAAK;AAE7C,MAAI,KAAK,WAAW,KAAK,OAAQ,QAAO;AAExC,SAAO,OAAO,gBAAgB,MAAM,IAAI;AAC5C;AASA,cAAc;",
6
+ "names": []
7
+ }
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var rate_limit_exports = {};
20
+ __export(rate_limit_exports, {
21
+ MemoryRateLimitStore: () => MemoryRateLimitStore,
22
+ createRateLimiter: () => createRateLimiter
23
+ });
24
+ module.exports = __toCommonJS(rate_limit_exports);
25
+ class MemoryRateLimitStore {
26
+ hits = /* @__PURE__ */ new Map();
27
+ async increment(key, windowMs) {
28
+ const now = Date.now();
29
+ const record = this.hits.get(key);
30
+ if (!record || now > record.resetTime) {
31
+ const newRecord = { count: 1, resetTime: now + windowMs };
32
+ this.hits.set(key, newRecord);
33
+ return newRecord;
34
+ }
35
+ record.count += 1;
36
+ return record;
37
+ }
38
+ async getRateLimit(key) {
39
+ const now = Date.now();
40
+ const record = this.hits.get(key);
41
+ if (!record || now > record.resetTime) return null;
42
+ return record;
43
+ }
44
+ }
45
+ function createRateLimiter(adapter, options) {
46
+ if (!options) return null;
47
+ const memoryFallback = new MemoryRateLimitStore();
48
+ let hasWarned = false;
49
+ return {
50
+ async increment(key, overrideWindowMs) {
51
+ let result;
52
+ const windowMs = overrideWindowMs || options.windowMs;
53
+ if (adapter.incrementRateLimit) {
54
+ result = await adapter.incrementRateLimit(key, windowMs);
55
+ } else {
56
+ if (!hasWarned && process.env.NODE_ENV !== "production") {
57
+ console.warn("[Kroxt] Warning: AuthAdapter does not implement incrementRateLimit. Falling back to in-memory rate limiter. This is not recommended for serverless or multi-instance deployments.");
58
+ hasWarned = true;
59
+ }
60
+ result = await memoryFallback.increment(key, windowMs);
61
+ }
62
+ return {
63
+ success: result.count <= options.max,
64
+ limit: options.max,
65
+ remaining: Math.max(0, options.max - result.count),
66
+ reset: result.resetTime
67
+ };
68
+ },
69
+ async check(key) {
70
+ if (adapter.getRateLimit) {
71
+ return await adapter.getRateLimit(key);
72
+ }
73
+ return memoryFallback.getRateLimit(key);
74
+ }
75
+ };
76
+ }
77
+ // Annotate the CommonJS export names for ESM import in node:
78
+ 0 && (module.exports = {
79
+ MemoryRateLimitStore,
80
+ createRateLimiter
81
+ });
82
+ //# sourceMappingURL=rate-limit.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/auth/security/rate-limit.ts"],
4
+ "sourcesContent": ["import type { AuthAdapter } from \"../adapters/index.js\";\r\n\r\nexport interface RateLimitOptions {\r\n windowMs: number;\r\n max: number;\r\n}\r\n\r\nexport interface RateLimitResult {\r\n success: boolean;\r\n limit: number;\r\n remaining: number;\r\n reset: number;\r\n}\r\n\r\n/**\r\n * A zero-dependency in-memory store for rate limiting.\r\n * Used as a fallback if the provided AuthAdapter does not\r\n * implement `incrementRateLimit`.\r\n */\r\nexport class MemoryRateLimitStore {\r\n private hits = new Map<string, { count: number; resetTime: number }>();\r\n\r\n async increment(key: string, windowMs: number): Promise<{ count: number; resetTime: number }> {\r\n const now = Date.now();\r\n const record = this.hits.get(key);\r\n\r\n if (!record || now > record.resetTime) {\r\n const newRecord = { count: 1, resetTime: now + windowMs };\r\n this.hits.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 async getRateLimit(key: string): Promise<{ count: number; resetTime: number } | null> {\r\n const now = Date.now();\r\n const record = this.hits.get(key);\r\n if (!record || now > record.resetTime) return null;\r\n return record;\r\n }\r\n}\r\n\r\n/**\r\n * Creates a rate limiter function that uses the Database Adapter\r\n * for state tracking (if supported), or falls back to Memory.\r\n */\r\nexport function createRateLimiter(adapter: AuthAdapter<any>, options?: RateLimitOptions) {\r\n if (!options) return null;\r\n\r\n const memoryFallback = new MemoryRateLimitStore();\r\n let hasWarned = false;\r\n\r\n return {\r\n async increment(key: string, overrideWindowMs?: number): Promise<RateLimitResult> {\r\n let result;\r\n const windowMs = overrideWindowMs || options.windowMs;\r\n\r\n if (adapter.incrementRateLimit) {\r\n result = await adapter.incrementRateLimit(key, windowMs);\r\n } else {\r\n if (!hasWarned && process.env.NODE_ENV !== \"production\") {\r\n console.warn(\"[Kroxt] Warning: AuthAdapter does not implement incrementRateLimit. Falling back to in-memory rate limiter. This is not recommended for serverless or multi-instance deployments.\");\r\n hasWarned = true;\r\n }\r\n result = await memoryFallback.increment(key, windowMs);\r\n }\r\n\r\n return {\r\n success: result.count <= options.max,\r\n limit: options.max,\r\n remaining: Math.max(0, options.max - result.count),\r\n reset: result.resetTime,\r\n };\r\n },\r\n\r\n async check(key: string): Promise<{ count: number; resetTime: number } | null> {\r\n if (adapter.getRateLimit) {\r\n return await adapter.getRateLimit(key);\r\n }\r\n return memoryFallback.getRateLimit(key);\r\n }\r\n };\r\n}\r\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBO,MAAM,qBAAqB;AAAA,EACtB,OAAO,oBAAI,IAAkD;AAAA,EAErE,MAAM,UAAU,KAAa,UAAiE;AAC1F,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,KAAK,KAAK,IAAI,GAAG;AAEhC,QAAI,CAAC,UAAU,MAAM,OAAO,WAAW;AACnC,YAAM,YAAY,EAAE,OAAO,GAAG,WAAW,MAAM,SAAS;AACxD,WAAK,KAAK,IAAI,KAAK,SAAS;AAC5B,aAAO;AAAA,IACX;AAEA,WAAO,SAAS;AAChB,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,aAAa,KAAmE;AAClF,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,KAAK,KAAK,IAAI,GAAG;AAChC,QAAI,CAAC,UAAU,MAAM,OAAO,UAAW,QAAO;AAC9C,WAAO;AAAA,EACX;AACJ;AAMO,SAAS,kBAAkB,SAA2B,SAA4B;AACrF,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,iBAAiB,IAAI,qBAAqB;AAChD,MAAI,YAAY;AAEhB,SAAO;AAAA,IACH,MAAM,UAAU,KAAa,kBAAqD;AAC9E,UAAI;AACJ,YAAM,WAAW,oBAAoB,QAAQ;AAE7C,UAAI,QAAQ,oBAAoB;AAC5B,iBAAS,MAAM,QAAQ,mBAAmB,KAAK,QAAQ;AAAA,MAC3D,OAAO;AACH,YAAI,CAAC,aAAa,QAAQ,IAAI,aAAa,cAAc;AACrD,kBAAQ,KAAK,mLAAmL;AAChM,sBAAY;AAAA,QAChB;AACA,iBAAS,MAAM,eAAe,UAAU,KAAK,QAAQ;AAAA,MACzD;AAEA,aAAO;AAAA,QACH,SAAS,OAAO,SAAS,QAAQ;AAAA,QACjC,OAAO,QAAQ;AAAA,QACf,WAAW,KAAK,IAAI,GAAG,QAAQ,MAAM,OAAO,KAAK;AAAA,QACjD,OAAO,OAAO;AAAA,MAClB;AAAA,IACJ;AAAA,IAEA,MAAM,MAAM,KAAmE;AAC3E,UAAI,QAAQ,cAAc;AACtB,eAAO,MAAM,QAAQ,aAAa,GAAG;AAAA,MACzC;AACA,aAAO,eAAe,aAAa,GAAG;AAAA,IAC1C;AAAA,EACJ;AACJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,57 @@
1
+ class MemoryRateLimitStore {
2
+ hits = /* @__PURE__ */ new Map();
3
+ async increment(key, windowMs) {
4
+ const now = Date.now();
5
+ const record = this.hits.get(key);
6
+ if (!record || now > record.resetTime) {
7
+ const newRecord = { count: 1, resetTime: now + windowMs };
8
+ this.hits.set(key, newRecord);
9
+ return newRecord;
10
+ }
11
+ record.count += 1;
12
+ return record;
13
+ }
14
+ async getRateLimit(key) {
15
+ const now = Date.now();
16
+ const record = this.hits.get(key);
17
+ if (!record || now > record.resetTime) return null;
18
+ return record;
19
+ }
20
+ }
21
+ function createRateLimiter(adapter, options) {
22
+ if (!options) return null;
23
+ const memoryFallback = new MemoryRateLimitStore();
24
+ let hasWarned = false;
25
+ return {
26
+ async increment(key, overrideWindowMs) {
27
+ let result;
28
+ const windowMs = overrideWindowMs || options.windowMs;
29
+ if (adapter.incrementRateLimit) {
30
+ result = await adapter.incrementRateLimit(key, windowMs);
31
+ } else {
32
+ if (!hasWarned && process.env.NODE_ENV !== "production") {
33
+ console.warn("[Kroxt] Warning: AuthAdapter does not implement incrementRateLimit. Falling back to in-memory rate limiter. This is not recommended for serverless or multi-instance deployments.");
34
+ hasWarned = true;
35
+ }
36
+ result = await memoryFallback.increment(key, windowMs);
37
+ }
38
+ return {
39
+ success: result.count <= options.max,
40
+ limit: options.max,
41
+ remaining: Math.max(0, options.max - result.count),
42
+ reset: result.resetTime
43
+ };
44
+ },
45
+ async check(key) {
46
+ if (adapter.getRateLimit) {
47
+ return await adapter.getRateLimit(key);
48
+ }
49
+ return memoryFallback.getRateLimit(key);
50
+ }
51
+ };
52
+ }
53
+ export {
54
+ MemoryRateLimitStore,
55
+ createRateLimiter
56
+ };
57
+ //# sourceMappingURL=rate-limit.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/auth/security/rate-limit.ts"],
4
+ "sourcesContent": ["import type { AuthAdapter } from \"../adapters/index.js\";\r\n\r\nexport interface RateLimitOptions {\r\n windowMs: number;\r\n max: number;\r\n}\r\n\r\nexport interface RateLimitResult {\r\n success: boolean;\r\n limit: number;\r\n remaining: number;\r\n reset: number;\r\n}\r\n\r\n/**\r\n * A zero-dependency in-memory store for rate limiting.\r\n * Used as a fallback if the provided AuthAdapter does not\r\n * implement `incrementRateLimit`.\r\n */\r\nexport class MemoryRateLimitStore {\r\n private hits = new Map<string, { count: number; resetTime: number }>();\r\n\r\n async increment(key: string, windowMs: number): Promise<{ count: number; resetTime: number }> {\r\n const now = Date.now();\r\n const record = this.hits.get(key);\r\n\r\n if (!record || now > record.resetTime) {\r\n const newRecord = { count: 1, resetTime: now + windowMs };\r\n this.hits.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 async getRateLimit(key: string): Promise<{ count: number; resetTime: number } | null> {\r\n const now = Date.now();\r\n const record = this.hits.get(key);\r\n if (!record || now > record.resetTime) return null;\r\n return record;\r\n }\r\n}\r\n\r\n/**\r\n * Creates a rate limiter function that uses the Database Adapter\r\n * for state tracking (if supported), or falls back to Memory.\r\n */\r\nexport function createRateLimiter(adapter: AuthAdapter<any>, options?: RateLimitOptions) {\r\n if (!options) return null;\r\n\r\n const memoryFallback = new MemoryRateLimitStore();\r\n let hasWarned = false;\r\n\r\n return {\r\n async increment(key: string, overrideWindowMs?: number): Promise<RateLimitResult> {\r\n let result;\r\n const windowMs = overrideWindowMs || options.windowMs;\r\n\r\n if (adapter.incrementRateLimit) {\r\n result = await adapter.incrementRateLimit(key, windowMs);\r\n } else {\r\n if (!hasWarned && process.env.NODE_ENV !== \"production\") {\r\n console.warn(\"[Kroxt] Warning: AuthAdapter does not implement incrementRateLimit. Falling back to in-memory rate limiter. This is not recommended for serverless or multi-instance deployments.\");\r\n hasWarned = true;\r\n }\r\n result = await memoryFallback.increment(key, windowMs);\r\n }\r\n\r\n return {\r\n success: result.count <= options.max,\r\n limit: options.max,\r\n remaining: Math.max(0, options.max - result.count),\r\n reset: result.resetTime,\r\n };\r\n },\r\n\r\n async check(key: string): Promise<{ count: number; resetTime: number } | null> {\r\n if (adapter.getRateLimit) {\r\n return await adapter.getRateLimit(key);\r\n }\r\n return memoryFallback.getRateLimit(key);\r\n }\r\n };\r\n}\r\n"],
5
+ "mappings": "AAmBO,MAAM,qBAAqB;AAAA,EACtB,OAAO,oBAAI,IAAkD;AAAA,EAErE,MAAM,UAAU,KAAa,UAAiE;AAC1F,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,KAAK,KAAK,IAAI,GAAG;AAEhC,QAAI,CAAC,UAAU,MAAM,OAAO,WAAW;AACnC,YAAM,YAAY,EAAE,OAAO,GAAG,WAAW,MAAM,SAAS;AACxD,WAAK,KAAK,IAAI,KAAK,SAAS;AAC5B,aAAO;AAAA,IACX;AAEA,WAAO,SAAS;AAChB,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,aAAa,KAAmE;AAClF,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,KAAK,KAAK,IAAI,GAAG;AAChC,QAAI,CAAC,UAAU,MAAM,OAAO,UAAW,QAAO;AAC9C,WAAO;AAAA,EACX;AACJ;AAMO,SAAS,kBAAkB,SAA2B,SAA4B;AACrF,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,iBAAiB,IAAI,qBAAqB;AAChD,MAAI,YAAY;AAEhB,SAAO;AAAA,IACH,MAAM,UAAU,KAAa,kBAAqD;AAC9E,UAAI;AACJ,YAAM,WAAW,oBAAoB,QAAQ;AAE7C,UAAI,QAAQ,oBAAoB;AAC5B,iBAAS,MAAM,QAAQ,mBAAmB,KAAK,QAAQ;AAAA,MAC3D,OAAO;AACH,YAAI,CAAC,aAAa,QAAQ,IAAI,aAAa,cAAc;AACrD,kBAAQ,KAAK,mLAAmL;AAChM,sBAAY;AAAA,QAChB;AACA,iBAAS,MAAM,eAAe,UAAU,KAAK,QAAQ;AAAA,MACzD;AAEA,aAAO;AAAA,QACH,SAAS,OAAO,SAAS,QAAQ;AAAA,QACjC,OAAO,QAAQ;AAAA,QACf,WAAW,KAAK,IAAI,GAAG,QAAQ,MAAM,OAAO,KAAK;AAAA,QACjD,OAAO,OAAO;AAAA,MAClB;AAAA,IACJ;AAAA,IAEA,MAAM,MAAM,KAAmE;AAC3E,UAAI,QAAQ,cAAc;AACtB,eAAO,MAAM,QAAQ,aAAa,GAAG;AAAA,MACzC;AACA,aAAO,eAAe,aAAa,GAAG;AAAA,IAC1C;AAAA,EACJ;AACJ;",
6
+ "names": []
7
+ }