@contentgrowth/content-auth 0.6.1 → 0.6.3

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.
@@ -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 };
@@ -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-MDSQC2UU.js";
14
+ } from "../chunk-V434IQQD.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
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  authClient
3
- } from "./chunk-NKDKDBM2.js";
3
+ } from "./chunk-W5RBQBED.js";
4
4
 
5
5
  // src/frontend/components/react/AuthForm.tsx
6
6
  import { useState, useRef, useEffect } from "react";
@@ -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;
@@ -657,6 +657,8 @@ export {
657
657
  verifyTurnstile,
658
658
  normalizeEmail,
659
659
  isGmailAddress,
660
+ hashPassword,
661
+ verifyPassword,
660
662
  schema_exports,
661
663
  getInvitationLink,
662
664
  getSessionToken,
@@ -1,12 +1,16 @@
1
1
  // src/frontend/client.ts
2
2
  import { createAuthClient } from "better-auth/react";
3
3
  import { organizationClient } from "better-auth/client/plugins";
4
- var createClient = (baseUrl) => {
4
+ var createClient = (baseUrl, options = {}) => {
5
+ const { refetchOnWindowFocus = false } = options;
5
6
  return createAuthClient({
6
7
  baseURL: baseUrl,
7
8
  plugins: [
8
9
  organizationClient()
9
- ]
10
+ ],
11
+ sessionOptions: {
12
+ refetchOnWindowFocus
13
+ }
10
14
  });
11
15
  };
12
16
  var authClient = createClient();
@@ -3,7 +3,21 @@ import * as _better_fetch_fetch from '@better-fetch/fetch';
3
3
  import * as better_auth from 'better-auth';
4
4
  import * as better_auth_plugins from 'better-auth/plugins';
5
5
 
6
- declare const createClient: (baseUrl?: string) => {
6
+ interface ContentAuthClientOptions {
7
+ /**
8
+ * Controls whether the session is re-fetched when the browser window
9
+ * regains focus (e.g. lock/unlock screen, switching apps in a PWA).
10
+ *
11
+ * Defaults to `false`. Re-fetching on focus causes a React re-render
12
+ * cascade that can interrupt active client-side sessions (e.g. vocabulary
13
+ * review flows) by triggering component remounts.
14
+ *
15
+ * Set to `true` only for apps that need the session to be always
16
+ * up-to-date immediately after the user returns to the tab.
17
+ */
18
+ refetchOnWindowFocus?: boolean;
19
+ }
20
+ declare const createClient: (baseUrl?: string, options?: ContentAuthClientOptions) => {
7
21
  useActiveOrganization: () => {
8
22
  data: better_auth.Prettify<{
9
23
  id: string;
@@ -3194,4 +3208,4 @@ declare const authClient: {
3194
3208
  };
3195
3209
  };
3196
3210
 
3197
- export { authClient, createClient };
3211
+ export { type ContentAuthClientOptions, authClient, createClient };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  authClient,
3
3
  createClient
4
- } from "../chunk-NKDKDBM2.js";
4
+ } from "../chunk-W5RBQBED.js";
5
5
  import "../chunk-R5U7XKVJ.js";
6
6
  export {
7
7
  authClient,
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { authClient } from './client.js';
3
- export { createClient } from './client.js';
3
+ export { ContentAuthClientOptions, createClient } from './client.js';
4
4
  import 'nanostores';
5
5
  import '@better-fetch/fetch';
6
6
  import 'better-auth';
@@ -7,11 +7,11 @@ import {
7
7
  PasswordChanger,
8
8
  ProfileEditor,
9
9
  ResetPasswordForm
10
- } from "../chunk-AHFGJJZD.js";
10
+ } from "../chunk-EDNWJGKD.js";
11
11
  import {
12
12
  authClient,
13
13
  createClient
14
- } from "../chunk-NKDKDBM2.js";
14
+ } from "../chunk-W5RBQBED.js";
15
15
  import "../chunk-R5U7XKVJ.js";
16
16
  export {
17
17
  AuthForm,
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
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
- export { authClient, createClient } from './frontend/client.js';
3
+ export { ContentAuthClientOptions, authClient, createClient } from './frontend/client.js';
4
4
  export * from 'better-auth';
5
5
  export { Hono } from 'hono';
6
6
  import 'hono/types';
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-MDSQC2UU.js";
14
+ } from "./chunk-V434IQQD.js";
13
15
  import {
14
16
  AuthForm,
15
17
  CreateOrganizationForm,
@@ -19,11 +21,11 @@ import {
19
21
  PasswordChanger,
20
22
  ProfileEditor,
21
23
  ResetPasswordForm
22
- } from "./chunk-AHFGJJZD.js";
24
+ } from "./chunk-EDNWJGKD.js";
23
25
  import {
24
26
  authClient,
25
27
  createClient
26
- } from "./chunk-NKDKDBM2.js";
28
+ } from "./chunk-W5RBQBED.js";
27
29
  import "./chunk-R5U7XKVJ.js";
28
30
  export {
29
31
  AuthForm,
@@ -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.1",
3
+ "version": "0.6.3",
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",