kroxt 1.1.3 → 1.1.4

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 (44) hide show
  1. package/README.md +1 -1
  2. package/dist/adapter.cjs +17 -0
  3. package/dist/adapter.cjs.map +7 -0
  4. package/{dist-lib → dist}/adapter.d.ts +1 -0
  5. package/dist/adapter.d.ts.map +1 -0
  6. package/dist/adapter.js +1 -0
  7. package/dist/adapter.js.map +7 -0
  8. package/dist/core.cjs +113 -0
  9. package/dist/core.cjs.map +7 -0
  10. package/{dist-lib → dist}/core.d.ts +3 -2
  11. package/dist/core.d.ts.map +1 -0
  12. package/dist/core.js +78 -0
  13. package/dist/core.js.map +7 -0
  14. package/dist/index.cjs +42 -0
  15. package/dist/index.cjs.map +7 -0
  16. package/{dist-lib → dist}/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +12 -0
  19. package/dist/index.js.map +7 -0
  20. package/dist/memoryAdapter.cjs +55 -0
  21. package/dist/memoryAdapter.cjs.map +7 -0
  22. package/{dist-lib → dist}/memoryAdapter.d.ts +1 -0
  23. package/dist/memoryAdapter.d.ts.map +1 -0
  24. package/dist/memoryAdapter.js +31 -0
  25. package/dist/memoryAdapter.js.map +7 -0
  26. package/dist/providers.cjs +50 -0
  27. package/dist/providers.cjs.map +7 -0
  28. package/{dist-lib → dist}/providers.d.ts +1 -0
  29. package/dist/providers.d.ts.map +1 -0
  30. package/dist/providers.js +25 -0
  31. package/dist/providers.js.map +7 -0
  32. package/dist/security.cjs +55 -0
  33. package/dist/security.cjs.map +7 -0
  34. package/{dist-lib → dist}/security.d.ts +1 -0
  35. package/dist/security.d.ts.map +1 -0
  36. package/dist/security.js +20 -0
  37. package/dist/security.js.map +7 -0
  38. package/package.json +27 -16
  39. package/dist-lib/adapter.js +0 -1
  40. package/dist-lib/core.js +0 -103
  41. package/dist-lib/index.js +0 -4
  42. package/dist-lib/memoryAdapter.js +0 -34
  43. package/dist-lib/providers.js +0 -16
  44. package/dist-lib/security.js +0 -29
package/README.md CHANGED
@@ -12,7 +12,7 @@ A framework-agnostic, modular authentication engine for modern TypeScript applic
12
12
  - 🌶️ **Password Peppering**: Server-side pepper support for enhanced hash protection.
13
13
  - 🛡️ **Timing Attack Protection**: Built-in safeguards against side-channel analysis during login.
14
14
  - ✅ **Zod Schema Support**: Perfectly preserves and types your user metadata.
15
- - 🚀 **ESM First**: Native support for NodeNext module resolution.
15
+ - 🌍 **Dual ESM/CJS Support**: Native support for both modern ESM (`import`) and CommonJS (`require`).
16
16
 
17
17
  ## Installation
18
18
 
@@ -0,0 +1,17 @@
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 __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var adapter_exports = {};
16
+ module.exports = __toCommonJS(adapter_exports);
17
+ //# sourceMappingURL=adapter.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/auth/adapter.ts"],
4
+ "sourcesContent": ["export interface BaseUser {\r\n id: string;\r\n email: string;\r\n passwordHash?: string;\r\n role?: string;\r\n}\r\n\r\n// Allows any extended fields natively (like nin, bvn, maritalStatus, etc.)\r\nexport type User<TExtended = Record<string, any>> = BaseUser & TExtended;\r\n\r\nexport interface AuthAdapter<TUser = User> {\r\n createUser: (data: any) => Promise<TUser>;\r\n findUserByEmail: (email: string) => Promise<TUser | null>;\r\n findUserById: (id: string) => Promise<TUser | null>;\r\n linkOAuthAccount: (userId: string, provider: string, providerId: string) => Promise<void>;\r\n}\r\n"],
5
+ "mappings": ";;;;;;;;;;;;;;AAAA;AAAA;",
6
+ "names": []
7
+ }
@@ -11,3 +11,4 @@ export interface AuthAdapter<TUser = User> {
11
11
  findUserById: (id: string) => Promise<TUser | null>;
12
12
  linkOAuthAccount: (userId: string, provider: string, providerId: string) => Promise<void>;
13
13
  }
14
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/auth/adapter.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAGD,MAAM,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,QAAQ,GAAG,SAAS,CAAC;AAEzE,MAAM,WAAW,WAAW,CAAC,KAAK,GAAG,IAAI;IACvC,UAAU,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1C,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAC1D,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACpD,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3F"}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
package/dist/core.cjs ADDED
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var core_exports = {};
30
+ __export(core_exports, {
31
+ createAuth: () => createAuth,
32
+ generateSecret: () => generateSecret
33
+ });
34
+ module.exports = __toCommonJS(core_exports);
35
+ var argon2 = __toESM(require("argon2"), 1);
36
+ var import_jose = require("jose");
37
+ var import_crypto = __toESM(require("crypto"), 1);
38
+ function createAuth(options) {
39
+ const { adapter, secret, pepper, session, providers } = options;
40
+ const encodedSecret = typeof secret === "string" ? new TextEncoder().encode(secret) : secret;
41
+ const expiration = session?.expires || "1h";
42
+ const refreshExpiration = session?.refreshExpires || "7d";
43
+ async function generateToken(user, type = "access") {
44
+ let payload = { sub: user.id, role: user.role, type };
45
+ if (options.jwt?.payload) {
46
+ const customPayload = options.jwt.payload(user, type);
47
+ payload = { ...payload, ...customPayload };
48
+ }
49
+ return new import_jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(type === "access" ? expiration : refreshExpiration).sign(encodedSecret);
50
+ }
51
+ async function verifyToken(token, expectedType = "access") {
52
+ try {
53
+ const { payload } = await (0, import_jose.jwtVerify)(token, encodedSecret);
54
+ if (payload.type !== expectedType) return null;
55
+ return payload;
56
+ } catch (e) {
57
+ return null;
58
+ }
59
+ }
60
+ async function refresh(refreshToken) {
61
+ const payload = await verifyToken(refreshToken, "refresh");
62
+ if (!payload || !payload.sub) {
63
+ throw new Error("Invalid or expired refresh token");
64
+ }
65
+ const user = await adapter.findUserById(payload.sub);
66
+ if (!user) {
67
+ throw new Error("User not found");
68
+ }
69
+ const accessToken = await generateToken(user, "access");
70
+ return { accessToken };
71
+ }
72
+ async function signup(userData, password) {
73
+ let dataToSave = { ...userData };
74
+ if (password) {
75
+ const passwordWithPepper = pepper ? `${password}${pepper}` : password;
76
+ dataToSave.passwordHash = await argon2.hash(passwordWithPepper);
77
+ }
78
+ const newUser = await adapter.createUser(dataToSave);
79
+ const accessToken = await generateToken(newUser, "access");
80
+ const refreshToken = await generateToken(newUser, "refresh");
81
+ return { user: newUser, accessToken, refreshToken };
82
+ }
83
+ async function loginWithPassword(email, password) {
84
+ const user = await adapter.findUserByEmail(email);
85
+ const dummyHash = "$argon2id$v=19$m=65536,t=3,p=4$c29tZXNhbHQ$RytpInY7i6C9M5l0D4n8Q+7j/J+i";
86
+ const targetHash = user?.passwordHash || dummyHash;
87
+ const passwordWithPepper = pepper ? `${password}${pepper}` : password;
88
+ const isValid = await argon2.verify(targetHash, passwordWithPepper);
89
+ if (!user || !user.passwordHash || !isValid) {
90
+ throw new Error("Invalid credentials");
91
+ }
92
+ const accessToken = await generateToken(user, "access");
93
+ const refreshToken = await generateToken(user, "refresh");
94
+ return { user, accessToken, refreshToken };
95
+ }
96
+ return {
97
+ signup,
98
+ loginWithPassword,
99
+ refresh,
100
+ verifyToken,
101
+ generateToken,
102
+ _providers: providers
103
+ };
104
+ }
105
+ function generateSecret(length = 32) {
106
+ return import_crypto.default.getRandomValues(new Uint8Array(length));
107
+ }
108
+ // Annotate the CommonJS export names for ESM import in node:
109
+ 0 && (module.exports = {
110
+ createAuth,
111
+ generateSecret
112
+ });
113
+ //# sourceMappingURL=core.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/auth/core.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 \"./adapter.js\";\r\nimport type { Provider } from \"./providers.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 };\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}\r\n\r\nexport function createAuth(options: CreateAuthOptions) {\r\n const { adapter, secret, pepper, session, providers } = 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 /**\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 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 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 const accessToken = await generateToken(user, \"access\");\r\n return { accessToken };\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 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) {\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 return {\r\n signup,\r\n loginWithPassword,\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;AAwBZ,SAAS,WAAW,SAA4B;AACnD,QAAM,EAAE,SAAS,QAAQ,QAAQ,SAAS,UAAU,IAAI;AACxD,QAAM,gBAAgB,OAAO,WAAW,WAAW,IAAI,YAAY,EAAE,OAAO,MAAM,IAAI;AACtF,QAAM,aAAa,SAAS,WAAW;AACvC,QAAM,oBAAoB,SAAS,kBAAkB;AAKrD,iBAAe,cAAc,MAAiB,OAA6B,UAAU;AACjF,QAAI,UAA+B,EAAE,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM,KAAK;AAEzE,QAAI,QAAQ,KAAK,SAAS;AACtB,YAAM,gBAAgB,QAAQ,IAAI,QAAQ,MAAM,IAAI;AACpD,gBAAU,EAAE,GAAG,SAAS,GAAG,cAAc;AAAA,IAC7C;AAEA,WAAO,IAAI,oBAAQ,OAAO,EACrB,mBAAmB,EAAE,KAAK,QAAQ,CAAC,EACnC,YAAY,EACZ,kBAAkB,SAAS,WAAW,aAAa,iBAAiB,EACpE,KAAK,aAAa;AAAA,EAC3B;AAMA,iBAAe,YAAY,OAAe,eAAqC,UAAU;AACrF,QAAI;AACA,YAAM,EAAE,QAAQ,IAAI,UAAM,uBAAU,OAAO,aAAa;AACxD,UAAI,QAAQ,SAAS,aAAc,QAAO;AAC1C,aAAO;AAAA,IACX,SAAS,GAAG;AACR,aAAO;AAAA,IACX;AAAA,EACJ;AAKA,iBAAe,QAAQ,cAAsB;AACzC,UAAM,UAAU,MAAM,YAAY,cAAc,SAAS;AACzD,QAAI,CAAC,WAAW,CAAC,QAAQ,KAAK;AAC1B,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACtD;AAEA,UAAM,OAAO,MAAM,QAAQ,aAAa,QAAQ,GAAa;AAC7D,QAAI,CAAC,MAAM;AACP,YAAM,IAAI,MAAM,gBAAgB;AAAA,IACpC;AAEA,UAAM,cAAc,MAAM,cAAc,MAAM,QAAQ;AACtD,WAAO,EAAE,YAAY;AAAA,EACzB;AAMA,iBAAe,OAAO,UAAiC,UAAmB;AACtE,QAAI,aAAa,EAAE,GAAG,SAAS;AAE/B,QAAI,UAAU;AACV,YAAM,qBAAqB,SAAS,GAAG,QAAQ,GAAG,MAAM,KAAK;AAC7D,iBAAW,eAAe,MAAM,OAAO,KAAK,kBAAkB;AAAA,IAClE;AAEA,UAAM,UAAU,MAAM,QAAQ,WAAW,UAAU;AACnD,UAAM,cAAc,MAAM,cAAc,SAAS,QAAQ;AACzD,UAAM,eAAe,MAAM,cAAc,SAAS,SAAS;AAE3D,WAAO,EAAE,MAAM,SAAS,aAAa,aAAa;AAAA,EACtD;AAMA,iBAAe,kBAAkB,OAAe,UAAkB;AAC9D,UAAM,OAAO,MAAM,QAAQ,gBAAgB,KAAK;AAIhD,UAAM,YAAY;AAClB,UAAM,aAAa,MAAM,gBAAgB;AACzC,UAAM,qBAAqB,SAAS,GAAG,QAAQ,GAAG,MAAM,KAAK;AAE7D,UAAM,UAAU,MAAM,OAAO,OAAO,YAAY,kBAAkB;AAElE,QAAI,CAAC,QAAQ,CAAC,KAAK,gBAAgB,CAAC,SAAS;AACzC,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACzC;AAEA,UAAM,cAAc,MAAM,cAAc,MAAM,QAAQ;AACtD,UAAM,eAAe,MAAM,cAAc,MAAM,SAAS;AAExD,WAAO,EAAE,MAAM,aAAa,aAAa;AAAA,EAC7C;AAEA,SAAO;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,EAChB;AACJ;AAMO,SAAS,eAAe,SAAiB,IAAgB;AAC5D,SAAO,cAAAA,QAAO,gBAAgB,IAAI,WAAW,MAAM,CAAC;AACxD;",
6
+ "names": ["crypto"]
7
+ }
@@ -33,12 +33,13 @@ export declare function createAuth(options: CreateAuthOptions): {
33
33
  refresh: (refreshToken: string) => Promise<{
34
34
  accessToken: string;
35
35
  }>;
36
- verifyToken: (token: string, expectedType?: "access" | "refresh") => Promise<import("jose").JWTPayload>;
36
+ verifyToken: (token: string, expectedType?: "access" | "refresh") => Promise<import("jose").JWTPayload | null>;
37
37
  generateToken: (user: User<any>, type?: "access" | "refresh") => Promise<string>;
38
- _providers: Provider[];
38
+ _providers: Provider[] | undefined;
39
39
  };
40
40
  /**
41
41
  * Utility to generate a high-entropy cryptographically secure secret.
42
42
  * Useful for initializing the 'secret' option in createAuth.
43
43
  */
44
44
  export declare function generateSecret(length?: number): Uint8Array;
45
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/auth/core.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,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;KACpC,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;CACL;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB;uBA4DjB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,aAAa,MAAM;;;;;+BAmBhC,MAAM,YAAY,MAAM;;;;;4BAtC3B,MAAM;;;yBAbT,MAAM,iBAAgB,QAAQ,GAAG,SAAS;0BAnBzC,IAAI,CAAC,GAAG,CAAC,SAAQ,QAAQ,GAAG,SAAS;;EAmG3E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,GAAE,MAAW,GAAG,UAAU,CAE9D"}
package/dist/core.js ADDED
@@ -0,0 +1,78 @@
1
+ import * as argon2 from "argon2";
2
+ import { SignJWT, jwtVerify } from "jose";
3
+ import crypto from "crypto";
4
+ function createAuth(options) {
5
+ const { adapter, secret, pepper, session, providers } = options;
6
+ const encodedSecret = typeof secret === "string" ? new TextEncoder().encode(secret) : secret;
7
+ const expiration = session?.expires || "1h";
8
+ const refreshExpiration = session?.refreshExpires || "7d";
9
+ async function generateToken(user, type = "access") {
10
+ let payload = { sub: user.id, role: user.role, type };
11
+ if (options.jwt?.payload) {
12
+ const customPayload = options.jwt.payload(user, type);
13
+ payload = { ...payload, ...customPayload };
14
+ }
15
+ return new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(type === "access" ? expiration : refreshExpiration).sign(encodedSecret);
16
+ }
17
+ async function verifyToken(token, expectedType = "access") {
18
+ try {
19
+ const { payload } = await jwtVerify(token, encodedSecret);
20
+ if (payload.type !== expectedType) return null;
21
+ return payload;
22
+ } catch (e) {
23
+ return null;
24
+ }
25
+ }
26
+ async function refresh(refreshToken) {
27
+ const payload = await verifyToken(refreshToken, "refresh");
28
+ if (!payload || !payload.sub) {
29
+ throw new Error("Invalid or expired refresh token");
30
+ }
31
+ const user = await adapter.findUserById(payload.sub);
32
+ if (!user) {
33
+ throw new Error("User not found");
34
+ }
35
+ const accessToken = await generateToken(user, "access");
36
+ return { accessToken };
37
+ }
38
+ async function signup(userData, password) {
39
+ let dataToSave = { ...userData };
40
+ if (password) {
41
+ const passwordWithPepper = pepper ? `${password}${pepper}` : password;
42
+ dataToSave.passwordHash = await argon2.hash(passwordWithPepper);
43
+ }
44
+ const newUser = await adapter.createUser(dataToSave);
45
+ const accessToken = await generateToken(newUser, "access");
46
+ const refreshToken = await generateToken(newUser, "refresh");
47
+ return { user: newUser, accessToken, refreshToken };
48
+ }
49
+ async function loginWithPassword(email, password) {
50
+ const user = await adapter.findUserByEmail(email);
51
+ const dummyHash = "$argon2id$v=19$m=65536,t=3,p=4$c29tZXNhbHQ$RytpInY7i6C9M5l0D4n8Q+7j/J+i";
52
+ const targetHash = user?.passwordHash || dummyHash;
53
+ const passwordWithPepper = pepper ? `${password}${pepper}` : password;
54
+ const isValid = await argon2.verify(targetHash, passwordWithPepper);
55
+ if (!user || !user.passwordHash || !isValid) {
56
+ throw new Error("Invalid credentials");
57
+ }
58
+ const accessToken = await generateToken(user, "access");
59
+ const refreshToken = await generateToken(user, "refresh");
60
+ return { user, accessToken, refreshToken };
61
+ }
62
+ return {
63
+ signup,
64
+ loginWithPassword,
65
+ refresh,
66
+ verifyToken,
67
+ generateToken,
68
+ _providers: providers
69
+ };
70
+ }
71
+ function generateSecret(length = 32) {
72
+ return crypto.getRandomValues(new Uint8Array(length));
73
+ }
74
+ export {
75
+ createAuth,
76
+ generateSecret
77
+ };
78
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/auth/core.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 \"./adapter.js\";\r\nimport type { Provider } from \"./providers.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 };\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}\r\n\r\nexport function createAuth(options: CreateAuthOptions) {\r\n const { adapter, secret, pepper, session, providers } = 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 /**\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 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 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 const accessToken = await generateToken(user, \"access\");\r\n return { accessToken };\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 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) {\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 return {\r\n signup,\r\n loginWithPassword,\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,YAAY,YAAY;AACxB,SAAS,SAAS,iBAAiB;AACnC,OAAO,YAAY;AAwBZ,SAAS,WAAW,SAA4B;AACnD,QAAM,EAAE,SAAS,QAAQ,QAAQ,SAAS,UAAU,IAAI;AACxD,QAAM,gBAAgB,OAAO,WAAW,WAAW,IAAI,YAAY,EAAE,OAAO,MAAM,IAAI;AACtF,QAAM,aAAa,SAAS,WAAW;AACvC,QAAM,oBAAoB,SAAS,kBAAkB;AAKrD,iBAAe,cAAc,MAAiB,OAA6B,UAAU;AACjF,QAAI,UAA+B,EAAE,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM,KAAK;AAEzE,QAAI,QAAQ,KAAK,SAAS;AACtB,YAAM,gBAAgB,QAAQ,IAAI,QAAQ,MAAM,IAAI;AACpD,gBAAU,EAAE,GAAG,SAAS,GAAG,cAAc;AAAA,IAC7C;AAEA,WAAO,IAAI,QAAQ,OAAO,EACrB,mBAAmB,EAAE,KAAK,QAAQ,CAAC,EACnC,YAAY,EACZ,kBAAkB,SAAS,WAAW,aAAa,iBAAiB,EACpE,KAAK,aAAa;AAAA,EAC3B;AAMA,iBAAe,YAAY,OAAe,eAAqC,UAAU;AACrF,QAAI;AACA,YAAM,EAAE,QAAQ,IAAI,MAAM,UAAU,OAAO,aAAa;AACxD,UAAI,QAAQ,SAAS,aAAc,QAAO;AAC1C,aAAO;AAAA,IACX,SAAS,GAAG;AACR,aAAO;AAAA,IACX;AAAA,EACJ;AAKA,iBAAe,QAAQ,cAAsB;AACzC,UAAM,UAAU,MAAM,YAAY,cAAc,SAAS;AACzD,QAAI,CAAC,WAAW,CAAC,QAAQ,KAAK;AAC1B,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACtD;AAEA,UAAM,OAAO,MAAM,QAAQ,aAAa,QAAQ,GAAa;AAC7D,QAAI,CAAC,MAAM;AACP,YAAM,IAAI,MAAM,gBAAgB;AAAA,IACpC;AAEA,UAAM,cAAc,MAAM,cAAc,MAAM,QAAQ;AACtD,WAAO,EAAE,YAAY;AAAA,EACzB;AAMA,iBAAe,OAAO,UAAiC,UAAmB;AACtE,QAAI,aAAa,EAAE,GAAG,SAAS;AAE/B,QAAI,UAAU;AACV,YAAM,qBAAqB,SAAS,GAAG,QAAQ,GAAG,MAAM,KAAK;AAC7D,iBAAW,eAAe,MAAM,OAAO,KAAK,kBAAkB;AAAA,IAClE;AAEA,UAAM,UAAU,MAAM,QAAQ,WAAW,UAAU;AACnD,UAAM,cAAc,MAAM,cAAc,SAAS,QAAQ;AACzD,UAAM,eAAe,MAAM,cAAc,SAAS,SAAS;AAE3D,WAAO,EAAE,MAAM,SAAS,aAAa,aAAa;AAAA,EACtD;AAMA,iBAAe,kBAAkB,OAAe,UAAkB;AAC9D,UAAM,OAAO,MAAM,QAAQ,gBAAgB,KAAK;AAIhD,UAAM,YAAY;AAClB,UAAM,aAAa,MAAM,gBAAgB;AACzC,UAAM,qBAAqB,SAAS,GAAG,QAAQ,GAAG,MAAM,KAAK;AAE7D,UAAM,UAAU,MAAM,OAAO,OAAO,YAAY,kBAAkB;AAElE,QAAI,CAAC,QAAQ,CAAC,KAAK,gBAAgB,CAAC,SAAS;AACzC,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACzC;AAEA,UAAM,cAAc,MAAM,cAAc,MAAM,QAAQ;AACtD,UAAM,eAAe,MAAM,cAAc,MAAM,SAAS;AAExD,WAAO,EAAE,MAAM,aAAa,aAAa;AAAA,EAC7C;AAEA,SAAO;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,EAChB;AACJ;AAMO,SAAS,eAAe,SAAiB,IAAgB;AAC5D,SAAO,OAAO,gBAAgB,IAAI,WAAW,MAAM,CAAC;AACxD;",
6
+ "names": []
7
+ }
package/dist/index.cjs ADDED
@@ -0,0 +1,42 @@
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 __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var index_exports = {};
21
+ __export(index_exports, {
22
+ GitHub: () => import_providers.GitHub,
23
+ Google: () => import_providers.Google,
24
+ createAuth: () => import_core.createAuth,
25
+ createMemoryAdapter: () => import_memoryAdapter.createMemoryAdapter,
26
+ generateSecret: () => import_core.generateSecret
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+ var import_providers = require("./providers.js");
30
+ var import_core = require("./core.js");
31
+ var import_memoryAdapter = require("./memoryAdapter.js");
32
+ __reExport(index_exports, require("./security.js"), module.exports);
33
+ // Annotate the CommonJS export names for ESM import in node:
34
+ 0 && (module.exports = {
35
+ GitHub,
36
+ Google,
37
+ createAuth,
38
+ createMemoryAdapter,
39
+ generateSecret,
40
+ ...require("./security.js")
41
+ });
42
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/auth/index.ts"],
4
+ "sourcesContent": ["export type { AuthAdapter, User, BaseUser } from \"./adapter.js\";\r\nexport { GitHub, Google } from \"./providers.js\";\r\nexport type { Provider, ProviderConfig } from \"./providers.js\";\r\nexport { createAuth, generateSecret } from \"./core.js\";\r\nexport type { CreateAuthOptions } from \"./core.js\";\r\nexport { createMemoryAdapter } from \"./memoryAdapter.js\";\r\nexport * from \"./security.js\";\r\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,uBAA+B;AAE/B,kBAA2C;AAE3C,2BAAoC;AACpC,0BAAc,0BANd;",
6
+ "names": []
7
+ }
@@ -5,3 +5,4 @@ export { createAuth, generateSecret } from "./core.js";
5
5
  export type { CreateAuthOptions } from "./core.js";
6
6
  export { createMemoryAdapter } from "./memoryAdapter.js";
7
7
  export * from "./security.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAChD,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACvD,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,cAAc,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ import { GitHub, Google } from "./providers.js";
2
+ import { createAuth, generateSecret } from "./core.js";
3
+ import { createMemoryAdapter } from "./memoryAdapter.js";
4
+ export * from "./security.js";
5
+ export {
6
+ GitHub,
7
+ Google,
8
+ createAuth,
9
+ createMemoryAdapter,
10
+ generateSecret
11
+ };
12
+ //# 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 \"./adapter.js\";\r\nexport { GitHub, Google } from \"./providers.js\";\r\nexport type { Provider, ProviderConfig } from \"./providers.js\";\r\nexport { createAuth, generateSecret } from \"./core.js\";\r\nexport type { CreateAuthOptions } from \"./core.js\";\r\nexport { createMemoryAdapter } from \"./memoryAdapter.js\";\r\nexport * from \"./security.js\";\r\n"],
5
+ "mappings": "AACA,SAAS,QAAQ,cAAc;AAE/B,SAAS,YAAY,sBAAsB;AAE3C,SAAS,2BAA2B;AACpC,cAAc;",
6
+ "names": []
7
+ }
@@ -0,0 +1,55 @@
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 memoryAdapter_exports = {};
20
+ __export(memoryAdapter_exports, {
21
+ createMemoryAdapter: () => createMemoryAdapter
22
+ });
23
+ module.exports = __toCommonJS(memoryAdapter_exports);
24
+ function createMemoryAdapter() {
25
+ const users = /* @__PURE__ */ new Map();
26
+ const accounts = /* @__PURE__ */ new Map();
27
+ return {
28
+ createUser: async (data) => {
29
+ const id = data.id || Date.now().toString();
30
+ const newUser = { ...data, id };
31
+ users.set(newUser.email, newUser);
32
+ return newUser;
33
+ },
34
+ findUserByEmail: async (email) => {
35
+ return users.get(email) || null;
36
+ },
37
+ findUserById: async (id) => {
38
+ for (const user of users.values()) {
39
+ if (user.id === id) {
40
+ return user;
41
+ }
42
+ }
43
+ return null;
44
+ },
45
+ linkOAuthAccount: async (userId, provider, providerId) => {
46
+ const accountId = `${provider}_${providerId}`;
47
+ accounts.set(accountId, { userId, provider, providerId });
48
+ }
49
+ };
50
+ }
51
+ // Annotate the CommonJS export names for ESM import in node:
52
+ 0 && (module.exports = {
53
+ createMemoryAdapter
54
+ });
55
+ //# sourceMappingURL=memoryAdapter.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/auth/memoryAdapter.ts"],
4
+ "sourcesContent": ["import type { AuthAdapter, User } from \"./adapter.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\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 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}\r\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOO,SAAS,sBAAqE;AACjF,QAAM,QAAQ,oBAAI,IAAmB;AACrC,QAAM,WAAW,oBAAI,IAAsE;AAE3F,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,kBAAkB,OAAO,QAAgB,UAAkB,eAAuB;AAC9E,YAAM,YAAY,GAAG,QAAQ,IAAI,UAAU;AAC3C,eAAS,IAAI,WAAW,EAAE,QAAQ,UAAU,WAAW,CAAC;AAAA,IAC5D;AAAA,EACJ;AACJ;",
6
+ "names": []
7
+ }
@@ -5,3 +5,4 @@ import type { AuthAdapter, User } from "./adapter.js";
5
5
  * All data is kept in memory and is lost when the server restarts.
6
6
  */
7
7
  export declare function createMemoryAdapter<TUser extends User = User>(): AuthAdapter<TUser>;
8
+ //# sourceMappingURL=memoryAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoryAdapter.d.ts","sourceRoot":"","sources":["../src/auth/memoryAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEtD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,IAAI,GAAG,IAAI,KAAK,WAAW,CAAC,KAAK,CAAC,CAiCnF"}
@@ -0,0 +1,31 @@
1
+ function createMemoryAdapter() {
2
+ const users = /* @__PURE__ */ new Map();
3
+ const accounts = /* @__PURE__ */ new Map();
4
+ return {
5
+ createUser: async (data) => {
6
+ const id = data.id || Date.now().toString();
7
+ const newUser = { ...data, id };
8
+ users.set(newUser.email, newUser);
9
+ return newUser;
10
+ },
11
+ findUserByEmail: async (email) => {
12
+ return users.get(email) || null;
13
+ },
14
+ findUserById: async (id) => {
15
+ for (const user of users.values()) {
16
+ if (user.id === id) {
17
+ return user;
18
+ }
19
+ }
20
+ return null;
21
+ },
22
+ linkOAuthAccount: async (userId, provider, providerId) => {
23
+ const accountId = `${provider}_${providerId}`;
24
+ accounts.set(accountId, { userId, provider, providerId });
25
+ }
26
+ };
27
+ }
28
+ export {
29
+ createMemoryAdapter
30
+ };
31
+ //# sourceMappingURL=memoryAdapter.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/auth/memoryAdapter.ts"],
4
+ "sourcesContent": ["import type { AuthAdapter, User } from \"./adapter.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\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 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}\r\n"],
5
+ "mappings": "AAOO,SAAS,sBAAqE;AACjF,QAAM,QAAQ,oBAAI,IAAmB;AACrC,QAAM,WAAW,oBAAI,IAAsE;AAE3F,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,kBAAkB,OAAO,QAAgB,UAAkB,eAAuB;AAC9E,YAAM,YAAY,GAAG,QAAQ,IAAI,UAAU;AAC3C,eAAS,IAAI,WAAW,EAAE,QAAQ,UAAU,WAAW,CAAC;AAAA,IAC5D;AAAA,EACJ;AACJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,50 @@
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 providers_exports = {};
20
+ __export(providers_exports, {
21
+ GitHub: () => GitHub,
22
+ Google: () => Google
23
+ });
24
+ module.exports = __toCommonJS(providers_exports);
25
+ var import_arctic = require("arctic");
26
+ function GitHub(config) {
27
+ return {
28
+ id: "github",
29
+ handler: new import_arctic.GitHub(config.clientId, config.clientSecret, null)
30
+ };
31
+ }
32
+ function Google(config) {
33
+ if (!config.redirectURI) {
34
+ throw new Error("redirectURI is required for Google OAuth provider");
35
+ }
36
+ return {
37
+ id: "google",
38
+ handler: new import_arctic.Google(
39
+ config.clientId,
40
+ config.clientSecret,
41
+ config.redirectURI
42
+ )
43
+ };
44
+ }
45
+ // Annotate the CommonJS export names for ESM import in node:
46
+ 0 && (module.exports = {
47
+ GitHub,
48
+ Google
49
+ });
50
+ //# sourceMappingURL=providers.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/auth/providers.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
+ }
@@ -9,3 +9,4 @@ export interface Provider {
9
9
  }
10
10
  export declare function GitHub(config: ProviderConfig): Provider;
11
11
  export declare function Google(config: ProviderConfig): Provider;
12
+ //# sourceMappingURL=providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../src/auth/providers.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,GAAG,CAAC;CAChB;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,QAAQ,CAKvD;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,QAAQ,CAYvD"}
@@ -0,0 +1,25 @@
1
+ import { GitHub as ArcticGitHub, Google as ArcticGoogle } from "arctic";
2
+ function GitHub(config) {
3
+ return {
4
+ id: "github",
5
+ handler: new ArcticGitHub(config.clientId, config.clientSecret, null)
6
+ };
7
+ }
8
+ function Google(config) {
9
+ if (!config.redirectURI) {
10
+ throw new Error("redirectURI is required for Google OAuth provider");
11
+ }
12
+ return {
13
+ id: "google",
14
+ handler: new ArcticGoogle(
15
+ config.clientId,
16
+ config.clientSecret,
17
+ config.redirectURI
18
+ )
19
+ };
20
+ }
21
+ export {
22
+ GitHub,
23
+ Google
24
+ };
25
+ //# sourceMappingURL=providers.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/auth/providers.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
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var security_exports = {};
30
+ __export(security_exports, {
31
+ generateCsrfToken: () => generateCsrfToken,
32
+ verifyCsrf: () => verifyCsrf
33
+ });
34
+ module.exports = __toCommonJS(security_exports);
35
+ var import_crypto = __toESM(require("crypto"), 1);
36
+ function generateCsrfToken() {
37
+ return import_crypto.default.randomBytes(32).toString("hex");
38
+ }
39
+ function verifyCsrf(tokenInRequest, tokenInCookie) {
40
+ 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
+ }
49
+ }
50
+ // Annotate the CommonJS export names for ESM import in node:
51
+ 0 && (module.exports = {
52
+ generateCsrfToken,
53
+ verifyCsrf
54
+ });
55
+ //# sourceMappingURL=security.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/auth/security.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 // Constant time comparison\r\n try {\r\n return crypto.timingSafeEqual(\r\n Buffer.from(tokenInRequest),\r\n Buffer.from(tokenInCookie)\r\n );\r\n } catch {\r\n return false;\r\n }\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"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAmB;AAMZ,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;AAG9C,MAAI;AACA,WAAO,cAAAA,QAAO;AAAA,MACV,OAAO,KAAK,cAAc;AAAA,MAC1B,OAAO,KAAK,aAAa;AAAA,IAC7B;AAAA,EACJ,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;",
6
+ "names": ["crypto"]
7
+ }
@@ -14,3 +14,4 @@ export declare function verifyCsrf(tokenInRequest: string, tokenInCookie: string
14
14
  * 2. Use a 'pepper' in createAuth to protect hashes.
15
15
  * 3. Implement rate limiting on /login and /register endpoints.
16
16
  */
17
+ //# sourceMappingURL=security.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/auth/security.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAYjF;AAED;;;;;GAKG"}
@@ -0,0 +1,20 @@
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
+ try {
8
+ return crypto.timingSafeEqual(
9
+ Buffer.from(tokenInRequest),
10
+ Buffer.from(tokenInCookie)
11
+ );
12
+ } catch {
13
+ return false;
14
+ }
15
+ }
16
+ export {
17
+ generateCsrfToken,
18
+ verifyCsrf
19
+ };
20
+ //# sourceMappingURL=security.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/auth/security.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 // Constant time comparison\r\n try {\r\n return crypto.timingSafeEqual(\r\n Buffer.from(tokenInRequest),\r\n Buffer.from(tokenInCookie)\r\n );\r\n } catch {\r\n return false;\r\n }\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"],
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;AAG9C,MAAI;AACA,WAAO,OAAO;AAAA,MACV,OAAO,KAAK,cAAc;AAAA,MAC1B,OAAO,KAAK,aAAa;AAAA,IAC7B;AAAA,EACJ,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,45 +1,56 @@
1
1
  {
2
2
  "name": "kroxt",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "license": "MIT",
5
5
  "description": "A framework-agnostic modular auth engine",
6
6
  "type": "module",
7
- "main": "./dist-lib/index.js",
8
- "module": "./dist-lib/index.js",
9
- "types": "./dist-lib/index.d.ts",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
10
  "exports": {
11
11
  ".": {
12
- "types": "./dist-lib/index.d.ts",
13
- "import": "./dist-lib/index.js"
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
14
15
  },
15
16
  "./adapter": {
16
- "types": "./dist-lib/adapter.d.ts",
17
- "import": "./dist-lib/adapter.js"
17
+ "types": "./dist/adapter.d.ts",
18
+ "import": "./dist/adapter.js",
19
+ "require": "./dist/adapter.cjs"
18
20
  },
19
21
  "./core": {
20
- "types": "./dist-lib/core.d.ts",
21
- "import": "./dist-lib/core.js"
22
+ "types": "./dist/core.d.ts",
23
+ "import": "./dist/core.js",
24
+ "require": "./dist/core.cjs"
22
25
  },
23
26
  "./providers": {
24
- "types": "./dist-lib/providers.d.ts",
25
- "import": "./dist-lib/providers.js"
27
+ "types": "./dist/providers.d.ts",
28
+ "import": "./dist/providers.js",
29
+ "require": "./dist/providers.cjs"
26
30
  },
27
31
  "./memoryAdapter": {
28
- "types": "./dist-lib/memoryAdapter.d.ts",
29
- "import": "./dist-lib/memoryAdapter.js"
32
+ "types": "./dist/memoryAdapter.d.ts",
33
+ "import": "./dist/memoryAdapter.js",
34
+ "require": "./dist/memoryAdapter.cjs"
30
35
  }
31
36
  },
32
37
  "files": [
33
- "dist-lib",
38
+ "dist",
34
39
  "README.md"
35
40
  ],
36
41
  "scripts": {
37
- "build": "npx tsc src/auth/index.ts --outDir dist-lib --module nodenext --declaration"
42
+ "build": "node build.js"
38
43
  },
39
44
  "dependencies": {
40
45
  "arctic": "^3.7.0",
41
46
  "argon2": "^0.44.0",
42
47
  "jose": "^6.2.1",
43
48
  "zod": "^3.23.8"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^25.5.0",
52
+ "esbuild": "^0.27.4",
53
+ "tsup": "^8.5.1",
54
+ "typescript": "^5.9.3"
44
55
  }
45
56
  }
@@ -1 +0,0 @@
1
- export {};
package/dist-lib/core.js DELETED
@@ -1,103 +0,0 @@
1
- import * as argon2 from "argon2";
2
- import { SignJWT, jwtVerify } from "jose";
3
- import crypto from "crypto";
4
- export function createAuth(options) {
5
- const { adapter, secret, pepper, session, providers } = options;
6
- const encodedSecret = typeof secret === "string" ? new TextEncoder().encode(secret) : secret;
7
- const expiration = session?.expires || "1h"; // Default access token to 1h
8
- const refreshExpiration = session?.refreshExpires || "7d";
9
- /**
10
- * Generates a stateless JWT for a user session
11
- */
12
- async function generateToken(user, type = "access") {
13
- let payload = { sub: user.id, role: user.role, type };
14
- if (options.jwt?.payload) {
15
- const customPayload = options.jwt.payload(user, type);
16
- payload = { ...payload, ...customPayload };
17
- }
18
- return new SignJWT(payload)
19
- .setProtectedHeader({ alg: "HS256" })
20
- .setIssuedAt()
21
- .setExpirationTime(type === "access" ? expiration : refreshExpiration)
22
- .sign(encodedSecret);
23
- }
24
- /**
25
- * Verifies a JWT and returns the payload.
26
- * Optionally checks for a specific token type (access/refresh).
27
- */
28
- async function verifyToken(token, expectedType = "access") {
29
- try {
30
- const { payload } = await jwtVerify(token, encodedSecret);
31
- if (payload.type !== expectedType)
32
- return null;
33
- return payload;
34
- }
35
- catch (e) {
36
- return null;
37
- }
38
- }
39
- /**
40
- * Refreshes an access token using a valid refresh token.
41
- */
42
- async function refresh(refreshToken) {
43
- const payload = await verifyToken(refreshToken, "refresh");
44
- if (!payload || !payload.sub) {
45
- throw new Error("Invalid or expired refresh token");
46
- }
47
- const user = await adapter.findUserById(payload.sub);
48
- if (!user) {
49
- throw new Error("User not found");
50
- }
51
- const accessToken = await generateToken(user, "access");
52
- return { accessToken };
53
- }
54
- /**
55
- * Signup with a new user payload.
56
- * Incorporates server-side pepper for password hashing if provided.
57
- */
58
- async function signup(userData, password) {
59
- let dataToSave = { ...userData };
60
- if (password) {
61
- const passwordWithPepper = pepper ? `${password}${pepper}` : password;
62
- dataToSave.passwordHash = await argon2.hash(passwordWithPepper);
63
- }
64
- const newUser = await adapter.createUser(dataToSave);
65
- const accessToken = await generateToken(newUser, "access");
66
- const refreshToken = await generateToken(newUser, "refresh");
67
- return { user: newUser, accessToken, refreshToken };
68
- }
69
- /**
70
- * Standard Email/Password Login.
71
- * Includes timing attack protection and password peppering.
72
- */
73
- async function loginWithPassword(email, password) {
74
- const user = await adapter.findUserByEmail(email);
75
- // Timing attack protection: Always verify a hash, even if user doesn't exist.
76
- // We use a dummy hash to keep execution time consistent.
77
- const dummyHash = "$argon2id$v=19$m=65536,t=3,p=4$c29tZXNhbHQ$RytpInY7i6C9M5l0D4n8Q+7j/J+i";
78
- const targetHash = user?.passwordHash || dummyHash;
79
- const passwordWithPepper = pepper ? `${password}${pepper}` : password;
80
- const isValid = await argon2.verify(targetHash, passwordWithPepper);
81
- if (!user || !user.passwordHash || !isValid) {
82
- throw new Error("Invalid credentials");
83
- }
84
- const accessToken = await generateToken(user, "access");
85
- const refreshToken = await generateToken(user, "refresh");
86
- return { user, accessToken, refreshToken };
87
- }
88
- return {
89
- signup,
90
- loginWithPassword,
91
- refresh,
92
- verifyToken,
93
- generateToken,
94
- _providers: providers
95
- };
96
- }
97
- /**
98
- * Utility to generate a high-entropy cryptographically secure secret.
99
- * Useful for initializing the 'secret' option in createAuth.
100
- */
101
- export function generateSecret(length = 32) {
102
- return crypto.getRandomValues(new Uint8Array(length));
103
- }
package/dist-lib/index.js DELETED
@@ -1,4 +0,0 @@
1
- export { GitHub, Google } from "./providers.js";
2
- export { createAuth, generateSecret } from "./core.js";
3
- export { createMemoryAdapter } from "./memoryAdapter.js";
4
- export * from "./security.js";
@@ -1,34 +0,0 @@
1
- /**
2
- * Creates an in-memory database adapter for the auth engine.
3
- * This is useful for testing, prototyping, or when you don't need persistent storage.
4
- * All data is kept in memory and is lost when the server restarts.
5
- */
6
- export function createMemoryAdapter() {
7
- const users = new Map();
8
- const accounts = new Map();
9
- return {
10
- createUser: async (data) => {
11
- // Auto-generate ID if not provided
12
- const id = data.id || Date.now().toString();
13
- const newUser = { ...data, id };
14
- // Store using email as the primary lookup key
15
- users.set(newUser.email, newUser);
16
- return newUser;
17
- },
18
- findUserByEmail: async (email) => {
19
- return users.get(email) || null;
20
- },
21
- findUserById: async (id) => {
22
- for (const user of users.values()) {
23
- if (user.id === id) {
24
- return user;
25
- }
26
- }
27
- return null;
28
- },
29
- linkOAuthAccount: async (userId, provider, providerId) => {
30
- const accountId = `${provider}_${providerId}`;
31
- accounts.set(accountId, { userId, provider, providerId });
32
- }
33
- };
34
- }
@@ -1,16 +0,0 @@
1
- import { GitHub as ArcticGitHub, Google as ArcticGoogle } from "arctic";
2
- export function GitHub(config) {
3
- return {
4
- id: "github",
5
- handler: new ArcticGitHub(config.clientId, config.clientSecret, null),
6
- };
7
- }
8
- export function Google(config) {
9
- if (!config.redirectURI) {
10
- throw new Error("redirectURI is required for Google OAuth provider");
11
- }
12
- return {
13
- id: "google",
14
- handler: new ArcticGoogle(config.clientId, config.clientSecret, config.redirectURI),
15
- };
16
- }
@@ -1,29 +0,0 @@
1
- import crypto from "crypto";
2
- /**
3
- * Generates a stateless CSRF token using the double-submit cookie pattern.
4
- * This is recommended for Express/Kroxt setups using cookies for sessions.
5
- */
6
- export function generateCsrfToken() {
7
- return crypto.randomBytes(32).toString("hex");
8
- }
9
- /**
10
- * Simple middleware-ready check for CSRF tokens.
11
- * Matches a token from the request body/headers against a cookie.
12
- */
13
- export function verifyCsrf(tokenInRequest, tokenInCookie) {
14
- if (!tokenInRequest || !tokenInCookie)
15
- return false;
16
- // Constant time comparison
17
- try {
18
- return crypto.timingSafeEqual(Buffer.from(tokenInRequest), Buffer.from(tokenInCookie));
19
- }
20
- catch {
21
- return false;
22
- }
23
- }
24
- /**
25
- * Security Recommendations for Kroxt:
26
- * 1. Always set cookies with: httpOnly: true, secure: true, sameSite: 'strict'
27
- * 2. Use a 'pepper' in createAuth to protect hashes.
28
- * 3. Implement rate limiting on /login and /register endpoints.
29
- */