@contentgrowth/content-auth 0.6.2 → 0.6.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.
- package/dist/backend/index.d.ts +7 -1
- package/dist/backend/index.js +5 -1
- package/dist/{chunk-MDSQC2UU.js → chunk-KUS3REJX.js} +114 -74
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -1
- package/package.json +1 -1
package/dist/backend/index.d.ts
CHANGED
|
@@ -47,6 +47,12 @@ declare function normalizeEmail(email: string): string;
|
|
|
47
47
|
*/
|
|
48
48
|
declare function isGmailAddress(email: string): boolean;
|
|
49
49
|
|
|
50
|
+
declare function hashPassword(password: string): Promise<string>;
|
|
51
|
+
declare function verifyPassword(passwordOrData: string | {
|
|
52
|
+
password: string;
|
|
53
|
+
hash: string;
|
|
54
|
+
}, storedHash?: string): Promise<boolean>;
|
|
55
|
+
|
|
50
56
|
declare const createUsersTable: (tableName?: string, fields?: Record<string, string>, additionalFields?: Record<string, any>) => drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
|
51
57
|
name: string;
|
|
52
58
|
schema: undefined;
|
|
@@ -904,4 +910,4 @@ declare const createAuthApp: (config: AuthConfig) => {
|
|
|
904
910
|
auth: better_auth.Auth<any>;
|
|
905
911
|
};
|
|
906
912
|
|
|
907
|
-
export { type AuthConfig, type EmailNormalizationConfig, type FieldAttribute, type SchemaMapping, type SignInTrackingConfig, type TableMapping, type TurnstileConfig, authMiddleware, createAuth, createAuthApp, getInvitationLink, getSessionToken, isGmailAddress, normalizeEmail, schema, verifyTurnstile };
|
|
913
|
+
export { type AuthConfig, type EmailNormalizationConfig, type FieldAttribute, type SchemaMapping, type SignInTrackingConfig, type TableMapping, type TurnstileConfig, authMiddleware, createAuth, createAuthApp, getInvitationLink, getSessionToken, hashPassword, isGmailAddress, normalizeEmail, schema, verifyPassword, verifyTurnstile };
|
package/dist/backend/index.js
CHANGED
|
@@ -5,11 +5,13 @@ import {
|
|
|
5
5
|
createAuthApp,
|
|
6
6
|
getInvitationLink,
|
|
7
7
|
getSessionToken,
|
|
8
|
+
hashPassword,
|
|
8
9
|
isGmailAddress,
|
|
9
10
|
normalizeEmail,
|
|
10
11
|
schema_exports,
|
|
12
|
+
verifyPassword,
|
|
11
13
|
verifyTurnstile
|
|
12
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-KUS3REJX.js";
|
|
13
15
|
import "../chunk-R5U7XKVJ.js";
|
|
14
16
|
export {
|
|
15
17
|
Hono,
|
|
@@ -18,8 +20,10 @@ export {
|
|
|
18
20
|
createAuthApp,
|
|
19
21
|
getInvitationLink,
|
|
20
22
|
getSessionToken,
|
|
23
|
+
hashPassword,
|
|
21
24
|
isGmailAddress,
|
|
22
25
|
normalizeEmail,
|
|
23
26
|
schema_exports as schema,
|
|
27
|
+
verifyPassword,
|
|
24
28
|
verifyTurnstile
|
|
25
29
|
};
|
|
@@ -101,6 +101,80 @@ function isGmailAddress(email) {
|
|
|
101
101
|
return lowerEmail.endsWith("@gmail.com") || lowerEmail.endsWith("@googlemail.com");
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
// src/backend/native-hashing.ts
|
|
105
|
+
async function hashPassword(password) {
|
|
106
|
+
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
107
|
+
const enc = new TextEncoder();
|
|
108
|
+
const keyMaterial = await crypto.subtle.importKey(
|
|
109
|
+
"raw",
|
|
110
|
+
enc.encode(password),
|
|
111
|
+
{ name: "PBKDF2" },
|
|
112
|
+
false,
|
|
113
|
+
["deriveBits", "deriveKey"]
|
|
114
|
+
);
|
|
115
|
+
const hashBuffer = await crypto.subtle.deriveBits(
|
|
116
|
+
{
|
|
117
|
+
name: "PBKDF2",
|
|
118
|
+
salt,
|
|
119
|
+
iterations: 1e5,
|
|
120
|
+
hash: "SHA-256"
|
|
121
|
+
},
|
|
122
|
+
keyMaterial,
|
|
123
|
+
256
|
|
124
|
+
// 32 bytes
|
|
125
|
+
);
|
|
126
|
+
const saltB64 = btoa(String.fromCharCode(...new Uint8Array(salt)));
|
|
127
|
+
const hashB64 = btoa(String.fromCharCode(...new Uint8Array(hashBuffer)));
|
|
128
|
+
return `pbkdf2:100000:${saltB64}:${hashB64}`;
|
|
129
|
+
}
|
|
130
|
+
async function verifyPassword(passwordOrData, storedHash) {
|
|
131
|
+
let password;
|
|
132
|
+
let hash;
|
|
133
|
+
if (typeof passwordOrData === "object" && passwordOrData !== null) {
|
|
134
|
+
password = passwordOrData.password;
|
|
135
|
+
hash = passwordOrData.hash;
|
|
136
|
+
} else {
|
|
137
|
+
password = passwordOrData;
|
|
138
|
+
hash = storedHash;
|
|
139
|
+
}
|
|
140
|
+
if (!hash) {
|
|
141
|
+
console.error("[Auth] verifyPassword called with empty/undefined hash");
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
const parts = hash.split(":");
|
|
145
|
+
if (parts.length !== 4) return false;
|
|
146
|
+
const [alg, iterationsStr, saltB64, hashB64] = parts;
|
|
147
|
+
if (alg !== "pbkdf2") return false;
|
|
148
|
+
const iterations = parseInt(iterationsStr, 10);
|
|
149
|
+
const salt = Uint8Array.from(atob(saltB64), (c) => c.charCodeAt(0));
|
|
150
|
+
const originalHash = Uint8Array.from(atob(hashB64), (c) => c.charCodeAt(0));
|
|
151
|
+
const enc = new TextEncoder();
|
|
152
|
+
const keyMaterial = await crypto.subtle.importKey(
|
|
153
|
+
"raw",
|
|
154
|
+
enc.encode(password),
|
|
155
|
+
{ name: "PBKDF2" },
|
|
156
|
+
false,
|
|
157
|
+
["deriveBits", "deriveKey"]
|
|
158
|
+
);
|
|
159
|
+
const hashBuffer = await crypto.subtle.deriveBits(
|
|
160
|
+
{
|
|
161
|
+
name: "PBKDF2",
|
|
162
|
+
salt,
|
|
163
|
+
iterations,
|
|
164
|
+
hash: "SHA-256"
|
|
165
|
+
},
|
|
166
|
+
keyMaterial,
|
|
167
|
+
256
|
|
168
|
+
);
|
|
169
|
+
const newHash = new Uint8Array(hashBuffer);
|
|
170
|
+
if (newHash.length !== originalHash.length) return false;
|
|
171
|
+
let result = 0;
|
|
172
|
+
for (let i = 0; i < newHash.length; i++) {
|
|
173
|
+
result |= newHash[i] ^ originalHash[i];
|
|
174
|
+
}
|
|
175
|
+
return result === 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
104
178
|
// src/backend/schema.ts
|
|
105
179
|
var schema_exports = {};
|
|
106
180
|
__export(schema_exports, {
|
|
@@ -244,80 +318,6 @@ var verifications = createVerificationsTable();
|
|
|
244
318
|
var members = createMembersTable("members", organizations, users);
|
|
245
319
|
var invitations = createInvitationsTable("invitations", organizations, users);
|
|
246
320
|
|
|
247
|
-
// src/backend/native-hashing.ts
|
|
248
|
-
async function hashPassword(password) {
|
|
249
|
-
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
250
|
-
const enc = new TextEncoder();
|
|
251
|
-
const keyMaterial = await crypto.subtle.importKey(
|
|
252
|
-
"raw",
|
|
253
|
-
enc.encode(password),
|
|
254
|
-
{ name: "PBKDF2" },
|
|
255
|
-
false,
|
|
256
|
-
["deriveBits", "deriveKey"]
|
|
257
|
-
);
|
|
258
|
-
const hashBuffer = await crypto.subtle.deriveBits(
|
|
259
|
-
{
|
|
260
|
-
name: "PBKDF2",
|
|
261
|
-
salt,
|
|
262
|
-
iterations: 1e5,
|
|
263
|
-
hash: "SHA-256"
|
|
264
|
-
},
|
|
265
|
-
keyMaterial,
|
|
266
|
-
256
|
|
267
|
-
// 32 bytes
|
|
268
|
-
);
|
|
269
|
-
const saltB64 = btoa(String.fromCharCode(...new Uint8Array(salt)));
|
|
270
|
-
const hashB64 = btoa(String.fromCharCode(...new Uint8Array(hashBuffer)));
|
|
271
|
-
return `pbkdf2:100000:${saltB64}:${hashB64}`;
|
|
272
|
-
}
|
|
273
|
-
async function verifyPassword(passwordOrData, storedHash) {
|
|
274
|
-
let password;
|
|
275
|
-
let hash;
|
|
276
|
-
if (typeof passwordOrData === "object" && passwordOrData !== null) {
|
|
277
|
-
password = passwordOrData.password;
|
|
278
|
-
hash = passwordOrData.hash;
|
|
279
|
-
} else {
|
|
280
|
-
password = passwordOrData;
|
|
281
|
-
hash = storedHash;
|
|
282
|
-
}
|
|
283
|
-
if (!hash) {
|
|
284
|
-
console.error("[Auth] verifyPassword called with empty/undefined hash");
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
const parts = hash.split(":");
|
|
288
|
-
if (parts.length !== 4) return false;
|
|
289
|
-
const [alg, iterationsStr, saltB64, hashB64] = parts;
|
|
290
|
-
if (alg !== "pbkdf2") return false;
|
|
291
|
-
const iterations = parseInt(iterationsStr, 10);
|
|
292
|
-
const salt = Uint8Array.from(atob(saltB64), (c) => c.charCodeAt(0));
|
|
293
|
-
const originalHash = Uint8Array.from(atob(hashB64), (c) => c.charCodeAt(0));
|
|
294
|
-
const enc = new TextEncoder();
|
|
295
|
-
const keyMaterial = await crypto.subtle.importKey(
|
|
296
|
-
"raw",
|
|
297
|
-
enc.encode(password),
|
|
298
|
-
{ name: "PBKDF2" },
|
|
299
|
-
false,
|
|
300
|
-
["deriveBits", "deriveKey"]
|
|
301
|
-
);
|
|
302
|
-
const hashBuffer = await crypto.subtle.deriveBits(
|
|
303
|
-
{
|
|
304
|
-
name: "PBKDF2",
|
|
305
|
-
salt,
|
|
306
|
-
iterations,
|
|
307
|
-
hash: "SHA-256"
|
|
308
|
-
},
|
|
309
|
-
keyMaterial,
|
|
310
|
-
256
|
|
311
|
-
);
|
|
312
|
-
const newHash = new Uint8Array(hashBuffer);
|
|
313
|
-
if (newHash.length !== originalHash.length) return false;
|
|
314
|
-
let result = 0;
|
|
315
|
-
for (let i = 0; i < newHash.length; i++) {
|
|
316
|
-
result |= newHash[i] ^ originalHash[i];
|
|
317
|
-
}
|
|
318
|
-
return result === 0;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
321
|
// src/backend/utils.ts
|
|
322
322
|
function getInvitationLink(data, baseUrl) {
|
|
323
323
|
let rawLink = data.link || data.url;
|
|
@@ -637,6 +637,44 @@ var createAuth = (config) => {
|
|
|
637
637
|
console.log("[ContentAuth-DEBUG] Other options passed to betterAuth():", Object.keys(otherOptions).join(", "));
|
|
638
638
|
console.log("[ContentAuth-DEBUG] ========================================");
|
|
639
639
|
}
|
|
640
|
+
auth.createUser = async (options) => {
|
|
641
|
+
const { email, password, name, emailVerified = false } = options;
|
|
642
|
+
const signupRequest = new Request(`${baseUrl}/api/auth/sign-up/email`, {
|
|
643
|
+
method: "POST",
|
|
644
|
+
headers: { "Content-Type": "application/json" },
|
|
645
|
+
body: JSON.stringify({
|
|
646
|
+
email: email.toLowerCase().trim(),
|
|
647
|
+
password,
|
|
648
|
+
name: name || email.split("@")[0],
|
|
649
|
+
callbackURL: "/"
|
|
650
|
+
})
|
|
651
|
+
});
|
|
652
|
+
const signupResponse = await auth.handler(signupRequest);
|
|
653
|
+
if (!signupResponse.ok) {
|
|
654
|
+
const errorData = await signupResponse.json();
|
|
655
|
+
throw new Error(`Failed to create user: ${JSON.stringify(errorData)}`);
|
|
656
|
+
}
|
|
657
|
+
const signupData = await signupResponse.json();
|
|
658
|
+
if (emailVerified && signupData.user?.id && rawDb?.prepare) {
|
|
659
|
+
await rawDb.prepare(
|
|
660
|
+
`UPDATE ${userTableName} SET ${userIdColumn === "id" ? "emailVerified" : schemaMapping?.user?.fields?.emailVerified || "emailVerified"} = 1 WHERE ${userIdColumn} = ?`
|
|
661
|
+
).bind(signupData.user.id).run();
|
|
662
|
+
}
|
|
663
|
+
return signupData.user;
|
|
664
|
+
};
|
|
665
|
+
auth.resetUserPassword = async (userId, newPassword) => {
|
|
666
|
+
if (!rawDb?.prepare) {
|
|
667
|
+
throw new Error("Password reset requires database access");
|
|
668
|
+
}
|
|
669
|
+
const hashedPassword = await hashPassword(newPassword);
|
|
670
|
+
const accountIdField = schemaMapping?.account?.fields?.userId || "userId";
|
|
671
|
+
const passwordField = schemaMapping?.account?.fields?.password || "password";
|
|
672
|
+
const updatedAtField = schemaMapping?.account?.fields?.updatedAt || "updatedAt";
|
|
673
|
+
await rawDb.prepare(
|
|
674
|
+
`UPDATE ${accountTableName} SET ${passwordField} = ?, ${updatedAtField} = ? WHERE ${accountIdField} = ? AND providerId = 'credential'`
|
|
675
|
+
).bind(hashedPassword, Date.now(), userId).run();
|
|
676
|
+
return { success: true };
|
|
677
|
+
};
|
|
640
678
|
return auth;
|
|
641
679
|
};
|
|
642
680
|
var authMiddleware = (auth) => {
|
|
@@ -657,6 +695,8 @@ export {
|
|
|
657
695
|
verifyTurnstile,
|
|
658
696
|
normalizeEmail,
|
|
659
697
|
isGmailAddress,
|
|
698
|
+
hashPassword,
|
|
699
|
+
verifyPassword,
|
|
660
700
|
schema_exports,
|
|
661
701
|
getInvitationLink,
|
|
662
702
|
getSessionToken,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AuthConfig, EmailNormalizationConfig, FieldAttribute, SchemaMapping, SignInTrackingConfig, TableMapping, TurnstileConfig, authMiddleware, createAuth, createAuthApp, getInvitationLink, getSessionToken, isGmailAddress, normalizeEmail, schema, verifyTurnstile } from './backend/index.js';
|
|
1
|
+
export { AuthConfig, EmailNormalizationConfig, FieldAttribute, SchemaMapping, SignInTrackingConfig, TableMapping, TurnstileConfig, authMiddleware, createAuth, createAuthApp, getInvitationLink, getSessionToken, hashPassword, isGmailAddress, normalizeEmail, schema, verifyPassword, verifyTurnstile } from './backend/index.js';
|
|
2
2
|
export { AuthForm, CreateOrganizationForm, ForgotPasswordForm, InviteMemberForm, OrganizationSwitcher, PasswordChanger, PasswordChangerProps, ProfileEditor, ProfileEditorProps, ResetPasswordForm } from './frontend/index.js';
|
|
3
3
|
export { ContentAuthClientOptions, authClient, createClient } from './frontend/client.js';
|
|
4
4
|
export * from 'better-auth';
|
package/dist/index.js
CHANGED
|
@@ -5,11 +5,13 @@ import {
|
|
|
5
5
|
createAuthApp,
|
|
6
6
|
getInvitationLink,
|
|
7
7
|
getSessionToken,
|
|
8
|
+
hashPassword,
|
|
8
9
|
isGmailAddress,
|
|
9
10
|
normalizeEmail,
|
|
10
11
|
schema_exports,
|
|
12
|
+
verifyPassword,
|
|
11
13
|
verifyTurnstile
|
|
12
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-KUS3REJX.js";
|
|
13
15
|
import {
|
|
14
16
|
AuthForm,
|
|
15
17
|
CreateOrganizationForm,
|
|
@@ -42,8 +44,10 @@ export {
|
|
|
42
44
|
createClient,
|
|
43
45
|
getInvitationLink,
|
|
44
46
|
getSessionToken,
|
|
47
|
+
hashPassword,
|
|
45
48
|
isGmailAddress,
|
|
46
49
|
normalizeEmail,
|
|
47
50
|
schema_exports as schema,
|
|
51
|
+
verifyPassword,
|
|
48
52
|
verifyTurnstile
|
|
49
53
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentgrowth/content-auth",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "Better Auth wrapper with UI components for Cloudflare Workers & Pages. Includes custom schema mapping, Turnstile bot protection, and email normalization.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|