@contentgrowth/content-auth 0.0.3 → 0.1.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.
@@ -1057,21 +1057,18 @@ interface AuthConfig {
1057
1057
  * Use 'postgres' or 'mysql' if passing a Drizzle instance for those DBs.
1058
1058
  */
1059
1059
  provider?: "sqlite" | "postgres" | "mysql";
1060
+ /**
1061
+ * Use Cloudflare native crypto.subtle for password hashing to avoid CPU limits.
1062
+ * Defaults to true. Set to false to use Better Auth's default (Scrypt/Argon2).
1063
+ */
1064
+ useCloudflareNativeHashing?: boolean;
1060
1065
  [key: string]: any;
1061
1066
  }
1062
- declare const createAuth: (config: AuthConfig) => better_auth.Auth<{
1063
- database: (options: better_auth.BetterAuthOptions) => better_auth.DBAdapter<better_auth.BetterAuthOptions>;
1064
- secret: string;
1065
- baseURL: string | undefined;
1066
- }>;
1067
+ declare const createAuth: (config: AuthConfig) => better_auth.Auth<any>;
1067
1068
  declare const authMiddleware: (auth: any) => (c: Context) => Promise<any>;
1068
1069
  declare const createAuthApp: (config: AuthConfig) => {
1069
1070
  app: Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
1070
- auth: better_auth.Auth<{
1071
- database: (options: better_auth.BetterAuthOptions) => better_auth.DBAdapter<better_auth.BetterAuthOptions>;
1072
- secret: string;
1073
- baseURL: string | undefined;
1074
- }>;
1071
+ auth: better_auth.Auth<any>;
1075
1072
  };
1076
1073
 
1077
1074
  export { type AuthConfig, authMiddleware, createAuth, createAuthApp, schema };
@@ -4,7 +4,7 @@ import {
4
4
  createAuth,
5
5
  createAuthApp,
6
6
  schema_exports
7
- } from "../chunk-526JE62U.js";
7
+ } from "../chunk-HPPCUGKY.js";
8
8
  import "../chunk-R5U7XKVJ.js";
9
9
  export {
10
10
  Hono,
@@ -89,6 +89,80 @@ var invitations = sqliteTable("invitations", {
89
89
  inviterId: text("inviterId").notNull().references(() => users.id, { onDelete: "cascade" })
90
90
  });
91
91
 
92
+ // src/backend/native-hashing.ts
93
+ async function hashPassword(password) {
94
+ const salt = crypto.getRandomValues(new Uint8Array(16));
95
+ const enc = new TextEncoder();
96
+ const keyMaterial = await crypto.subtle.importKey(
97
+ "raw",
98
+ enc.encode(password),
99
+ { name: "PBKDF2" },
100
+ false,
101
+ ["deriveBits", "deriveKey"]
102
+ );
103
+ const hashBuffer = await crypto.subtle.deriveBits(
104
+ {
105
+ name: "PBKDF2",
106
+ salt,
107
+ iterations: 1e5,
108
+ hash: "SHA-256"
109
+ },
110
+ keyMaterial,
111
+ 256
112
+ // 32 bytes
113
+ );
114
+ const saltB64 = btoa(String.fromCharCode(...new Uint8Array(salt)));
115
+ const hashB64 = btoa(String.fromCharCode(...new Uint8Array(hashBuffer)));
116
+ return `pbkdf2:100000:${saltB64}:${hashB64}`;
117
+ }
118
+ async function verifyPassword(passwordOrData, storedHash) {
119
+ let password;
120
+ let hash;
121
+ if (typeof passwordOrData === "object" && passwordOrData !== null) {
122
+ password = passwordOrData.password;
123
+ hash = passwordOrData.hash;
124
+ } else {
125
+ password = passwordOrData;
126
+ hash = storedHash;
127
+ }
128
+ if (!hash) {
129
+ console.error("[Auth] verifyPassword called with empty/undefined hash");
130
+ return false;
131
+ }
132
+ const parts = hash.split(":");
133
+ if (parts.length !== 4) return false;
134
+ const [alg, iterationsStr, saltB64, hashB64] = parts;
135
+ if (alg !== "pbkdf2") return false;
136
+ const iterations = parseInt(iterationsStr, 10);
137
+ const salt = Uint8Array.from(atob(saltB64), (c) => c.charCodeAt(0));
138
+ const originalHash = Uint8Array.from(atob(hashB64), (c) => c.charCodeAt(0));
139
+ const enc = new TextEncoder();
140
+ const keyMaterial = await crypto.subtle.importKey(
141
+ "raw",
142
+ enc.encode(password),
143
+ { name: "PBKDF2" },
144
+ false,
145
+ ["deriveBits", "deriveKey"]
146
+ );
147
+ const hashBuffer = await crypto.subtle.deriveBits(
148
+ {
149
+ name: "PBKDF2",
150
+ salt,
151
+ iterations,
152
+ hash: "SHA-256"
153
+ },
154
+ keyMaterial,
155
+ 256
156
+ );
157
+ const newHash = new Uint8Array(hashBuffer);
158
+ if (newHash.length !== originalHash.length) return false;
159
+ let result = 0;
160
+ for (let i = 0; i < newHash.length; i++) {
161
+ result |= newHash[i] ^ originalHash[i];
162
+ }
163
+ return result === 0;
164
+ }
165
+
92
166
  // src/backend/index.ts
93
167
  var createAuth = (config) => {
94
168
  let db;
@@ -98,7 +172,7 @@ var createAuth = (config) => {
98
172
  } else {
99
173
  db = config.database;
100
174
  }
101
- const { database, secret, baseUrl, provider: _, ...rest } = config;
175
+ const { database, secret, baseUrl, provider: _, useCloudflareNativeHashing = true, ...rest } = config;
102
176
  let adapterOptions = {
103
177
  provider,
104
178
  schema: {
@@ -111,11 +185,23 @@ var createAuth = (config) => {
111
185
  invitation: invitations
112
186
  }
113
187
  };
188
+ const emailConfig = rest.emailAndPassword || { enabled: true };
189
+ const { emailAndPassword, ...otherOptions } = rest;
190
+ const emailPasswordOptions = {
191
+ ...emailConfig
192
+ };
193
+ if (useCloudflareNativeHashing) {
194
+ emailPasswordOptions.password = {
195
+ hash: hashPassword,
196
+ verify: verifyPassword
197
+ };
198
+ }
114
199
  const auth = betterAuth({
115
200
  database: drizzleAdapter(db, adapterOptions),
116
201
  secret,
117
202
  baseURL: baseUrl,
118
- ...rest
203
+ emailAndPassword: emailPasswordOptions,
204
+ ...otherOptions
119
205
  });
120
206
  return auth;
121
207
  };
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  createAuth,
5
5
  createAuthApp,
6
6
  schema_exports
7
- } from "./chunk-526JE62U.js";
7
+ } from "./chunk-HPPCUGKY.js";
8
8
  import {
9
9
  AuthForm,
10
10
  CreateOrganizationForm,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentgrowth/content-auth",
3
- "version": "0.0.3",
3
+ "version": "0.1.0",
4
4
  "description": "Better Auth wrapper with UI components for Cloudflare Workers & Pages",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",