@objectstack/plugin-auth 6.2.0 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -217,13 +217,19 @@ declare class AuthManager {
217
217
  *
218
218
  * better-auth defaults to `@better-auth/utils/password.node`, which calls
219
219
  * `node:crypto.scrypt`. WebContainer polyfills that API incompletely and
220
- * signup throws `TypeError: y.run is not a function`. The pure-JS variant
221
- * at `@better-auth/utils/password` uses `@noble/hashes/scrypt` with
222
- * identical params (N=16384, r=16, p=1, dkLen=64) and emits the same
223
- * `{salt}:{keyHex}` format, so existing hashes remain verifiable.
220
+ * signup throws `TypeError: y.run is not a function`.
221
+ *
222
+ * We can't dynamic-import `@better-auth/utils/password` because that
223
+ * package's `exports` map gates the pure-JS build behind a non-`"node"`
224
+ * condition — Node-the-runtime (which WebContainer reports itself as)
225
+ * always resolves to `password.node.mjs`. So we reimplement the same hash
226
+ * here using `@noble/hashes/scrypt` directly, with byte-identical params
227
+ * (N=16384, r=16, p=1, dkLen=64) and the same `{saltHex}:{keyHex}` storage
228
+ * format. Hashes produced by either implementation verify against the
229
+ * other — no migration needed.
224
230
  *
225
231
  * Returns `undefined` outside WebContainer so production deployments keep
226
- * the native (fast) hasher and never load the JS fallback.
232
+ * the native (fast) hasher and never load `@noble/hashes`.
227
233
  */
228
234
  private resolvePasswordHasher;
229
235
  /**
package/dist/index.d.ts CHANGED
@@ -217,13 +217,19 @@ declare class AuthManager {
217
217
  *
218
218
  * better-auth defaults to `@better-auth/utils/password.node`, which calls
219
219
  * `node:crypto.scrypt`. WebContainer polyfills that API incompletely and
220
- * signup throws `TypeError: y.run is not a function`. The pure-JS variant
221
- * at `@better-auth/utils/password` uses `@noble/hashes/scrypt` with
222
- * identical params (N=16384, r=16, p=1, dkLen=64) and emits the same
223
- * `{salt}:{keyHex}` format, so existing hashes remain verifiable.
220
+ * signup throws `TypeError: y.run is not a function`.
221
+ *
222
+ * We can't dynamic-import `@better-auth/utils/password` because that
223
+ * package's `exports` map gates the pure-JS build behind a non-`"node"`
224
+ * condition — Node-the-runtime (which WebContainer reports itself as)
225
+ * always resolves to `password.node.mjs`. So we reimplement the same hash
226
+ * here using `@noble/hashes/scrypt` directly, with byte-identical params
227
+ * (N=16384, r=16, p=1, dkLen=64) and the same `{saltHex}:{keyHex}` storage
228
+ * format. Hashes produced by either implementation verify against the
229
+ * other — no migration needed.
224
230
  *
225
231
  * Returns `undefined` outside WebContainer so production deployments keep
226
- * the native (fast) hasher and never load the JS fallback.
232
+ * the native (fast) hasher and never load `@noble/hashes`.
227
233
  */
228
234
  private resolvePasswordHasher;
229
235
  /**
package/dist/index.js CHANGED
@@ -729,26 +729,49 @@ var AuthManager = class {
729
729
  *
730
730
  * better-auth defaults to `@better-auth/utils/password.node`, which calls
731
731
  * `node:crypto.scrypt`. WebContainer polyfills that API incompletely and
732
- * signup throws `TypeError: y.run is not a function`. The pure-JS variant
733
- * at `@better-auth/utils/password` uses `@noble/hashes/scrypt` with
734
- * identical params (N=16384, r=16, p=1, dkLen=64) and emits the same
735
- * `{salt}:{keyHex}` format, so existing hashes remain verifiable.
732
+ * signup throws `TypeError: y.run is not a function`.
733
+ *
734
+ * We can't dynamic-import `@better-auth/utils/password` because that
735
+ * package's `exports` map gates the pure-JS build behind a non-`"node"`
736
+ * condition — Node-the-runtime (which WebContainer reports itself as)
737
+ * always resolves to `password.node.mjs`. So we reimplement the same hash
738
+ * here using `@noble/hashes/scrypt` directly, with byte-identical params
739
+ * (N=16384, r=16, p=1, dkLen=64) and the same `{saltHex}:{keyHex}` storage
740
+ * format. Hashes produced by either implementation verify against the
741
+ * other — no migration needed.
736
742
  *
737
743
  * Returns `undefined` outside WebContainer so production deployments keep
738
- * the native (fast) hasher and never load the JS fallback.
744
+ * the native (fast) hasher and never load `@noble/hashes`.
739
745
  */
740
746
  async resolvePasswordHasher() {
741
747
  const isWebContainer = typeof globalThis !== "undefined" && (Boolean(globalThis.process?.versions?.webcontainer) || Boolean(globalThis.process?.env?.SHELL?.includes?.("jsh")) || Boolean(globalThis.process?.env?.STACKBLITZ));
742
748
  if (!isWebContainer) return void 0;
743
749
  try {
744
- const mod = await import("@better-auth/utils/password");
750
+ const { scryptAsync } = await import("@noble/hashes/scrypt.js");
751
+ const PARAMS = { N: 16384, r: 16, p: 1, dkLen: 64, maxmem: 128 * 16384 * 16 * 2 };
752
+ const toHex = (b) => {
753
+ let s = "";
754
+ for (let i = 0; i < b.length; i++) s += b[i].toString(16).padStart(2, "0");
755
+ return s;
756
+ };
757
+ const generateKey = (password, saltHex) => scryptAsync(password.normalize("NFKC"), saltHex, PARAMS);
745
758
  return {
746
- hash: (password) => mod.hashPassword(password),
747
- verify: ({ hash, password }) => mod.verifyPassword(hash, password)
759
+ hash: async (password) => {
760
+ const saltBytes = globalThis.crypto.getRandomValues(new Uint8Array(16));
761
+ const saltHex = toHex(saltBytes);
762
+ const key = await generateKey(password, saltHex);
763
+ return `${saltHex}:${toHex(key)}`;
764
+ },
765
+ verify: async ({ hash, password }) => {
766
+ const [saltHex, keyHex] = hash.split(":");
767
+ if (!saltHex || !keyHex) throw new Error("Invalid password hash");
768
+ const target = await generateKey(password, saltHex);
769
+ return toHex(target) === keyHex;
770
+ }
748
771
  };
749
772
  } catch (err) {
750
773
  console.warn(
751
- `[AuthManager] WebContainer detected but pure-JS password hasher unavailable: ${err?.message ?? err}. Falling back to default.`
774
+ `[AuthManager] WebContainer detected but pure-JS scrypt unavailable: ${err?.message ?? err}. Falling back to default.`
752
775
  );
753
776
  return void 0;
754
777
  }