@better-auth/infra 0.2.3 → 0.2.5

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
@@ -1,6 +1,6 @@
1
1
  import { EMAIL_TEMPLATES, EmailConfig, EmailTemplateId, EmailTemplateVariables, SendBulkEmailsOptions, SendBulkEmailsResult, SendEmailOptions, SendEmailResult, createEmailSender, sendBulkEmails, sendEmail } from "./email.mjs";
2
2
  import * as _$better_auth0 from "better-auth";
3
- import { Account, AuthContext, BetterAuthPlugin, GenericEndpointContext, HookEndpointContext, Session, User } from "better-auth";
3
+ import { Account, AuthContext, BetterAuthPlugin, GenericEndpointContext, Session, User } from "better-auth";
4
4
  import * as _$jose from "jose";
5
5
  import * as zod from "zod";
6
6
  import z$1 from "zod";
@@ -170,9 +170,6 @@ interface SecurityEvent {
170
170
  }
171
171
  type SecurityEventType = "unknown_device" | "credential_stuffing" | "impossible_travel" | "geo_blocked" | "bot_blocked" | "suspicious_ip_detected" | "velocity_exceeded" | "free_trial_abuse" | "compromised_password" | "stale_account_reactivation";
172
172
  //#endregion
173
- //#region src/validation/matchers.d.ts
174
- type Matcher = (context: HookEndpointContext) => boolean;
175
- //#endregion
176
173
  //#region src/sentinel/sentinel.d.ts
177
174
  declare const sentinel: (options?: SentinelOptions) => {
178
175
  id: "sentinel";
@@ -249,7 +246,7 @@ declare const sentinel: (options?: SentinelOptions) => {
249
246
  };
250
247
  hooks: {
251
248
  before: ({
252
- matcher: Matcher;
249
+ matcher: (context: _$better_auth0.HookEndpointContext) => boolean;
253
250
  handler: (inputContext: _$better_call0.MiddlewareInputContext<_$better_call0.MiddlewareOptions>) => Promise<{
254
251
  context: {
255
252
  method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
@@ -421,7 +418,7 @@ declare const sentinel: (options?: SentinelOptions) => {
421
418
  };
422
419
  } | undefined>;
423
420
  } | {
424
- matcher: Matcher;
421
+ matcher: (context: _$better_auth0.HookEndpointContext) => boolean;
425
422
  handler: (inputContext: _$better_call0.MiddlewareInputContext<_$better_call0.MiddlewareOptions>) => Promise<void>;
426
423
  })[];
427
424
  after: {
@@ -729,7 +726,7 @@ interface DashIdRow {
729
726
  id: string;
730
727
  }
731
728
  //#endregion
732
- //#region ../../node_modules/.bun/@better-auth+scim@1.6.1+f10cf570321458be/node_modules/@better-auth/scim/dist/index.d.mts
729
+ //#region ../../node_modules/.bun/@better-auth+scim@1.6.4+f772dc2362714ab9/node_modules/@better-auth/scim/dist/index.d.mts
733
730
  //#region src/types.d.ts
734
731
  interface SCIMProvider {
735
732
  id: string;
package/dist/index.mjs CHANGED
@@ -1398,7 +1398,7 @@ function createSecurityClient(apiUrl, apiKey, options, onSecurityEvent) {
1398
1398
  }
1399
1399
  //#endregion
1400
1400
  //#region src/validation/matchers.ts
1401
- const paths = [
1401
+ const all = new Set([
1402
1402
  "/sign-up/email",
1403
1403
  "/email-otp/verify-email",
1404
1404
  "/sign-in/email-otp",
@@ -1413,9 +1413,7 @@ const paths = [
1413
1413
  "/request-password-reset",
1414
1414
  "/send-verification-email",
1415
1415
  "/change-email"
1416
- ];
1417
- const all = new Set(paths);
1418
- new Set(paths.slice(1, 12));
1416
+ ]);
1419
1417
  /**
1420
1418
  * Path is one of `[
1421
1419
  * '/sign-up/email',
@@ -1687,7 +1685,6 @@ function createEmailHooks(options = {}) {
1687
1685
  emailValidationOptions: emailConfig
1688
1686
  }) : void 0, onDisposableEmail)] : []] };
1689
1687
  }
1690
- createEmailHooks();
1691
1688
  //#endregion
1692
1689
  //#region src/validation/phone.ts
1693
1690
  /**
@@ -2757,10 +2754,76 @@ const jwtValidateMiddleware = (options) => createAuthMiddleware(async (ctx) => {
2757
2754
  });
2758
2755
  //#endregion
2759
2756
  //#region src/version.ts
2760
- const PLUGIN_VERSION = "0.2.3";
2757
+ const PLUGIN_VERSION = "0.2.5";
2761
2758
  //#endregion
2762
2759
  //#region src/routes/auth/config.ts
2763
2760
  const PLUGIN_OPTIONS_EXCLUDE_KEYS = { stripe: new Set(["stripeClient"]) };
2761
+ const EXACT_SENSITIVE_STRING_KEYS_LOWER = new Set([...new Set([
2762
+ "accessToken",
2763
+ "apiKey",
2764
+ "apiSecret",
2765
+ "authorization",
2766
+ "authToken",
2767
+ "bearerToken",
2768
+ "clientSecret",
2769
+ "consumerSecret",
2770
+ "encPrivateKey",
2771
+ "encPrivateKeyPass",
2772
+ "encryptionKey",
2773
+ "encryptionSecret",
2774
+ "idToken",
2775
+ "password",
2776
+ "privateKey",
2777
+ "privateKeyPass",
2778
+ "refreshToken",
2779
+ "secret",
2780
+ "secretKey",
2781
+ "signingSecret",
2782
+ "stripeWebhookSecret",
2783
+ "webhookSecret"
2784
+ ])].map((k) => k.toLowerCase()));
2785
+ /**
2786
+ * Case-insensitive suffixes for unknown plugin options
2787
+ * Matching is more permissive to account for unknown plugin options
2788
+ */
2789
+ const SENSITIVE_KEY_SUFFIXES_LOWER = [
2790
+ "secret",
2791
+ "password",
2792
+ "passphrase",
2793
+ "privatekey",
2794
+ "keypass",
2795
+ "apikey",
2796
+ "token",
2797
+ "signingkey",
2798
+ "credentials",
2799
+ "authheader"
2800
+ ];
2801
+ const REDACTED_SIMPLE_STRING = "[REDACTED]";
2802
+ function snakeCaseToCamelCase(key) {
2803
+ return key.replace(/_([a-zA-Z])/g, (_, ch) => ch.toUpperCase());
2804
+ }
2805
+ function keyVariantsForMatching(key) {
2806
+ const trimmed = key.replace(/^[\s._-]+/, "");
2807
+ if (!trimmed) return [key];
2808
+ const out = new Set([key, trimmed]);
2809
+ if (trimmed.includes("_")) out.add(snakeCaseToCamelCase(trimmed));
2810
+ return [...out];
2811
+ }
2812
+ function shouldRedactSensitiveStringKey(key) {
2813
+ const variants = keyVariantsForMatching(key);
2814
+ const compactLower = key.replace(/[\s._-]/g, "").toLowerCase();
2815
+ const forms = new Set([compactLower]);
2816
+ for (const raw of variants) {
2817
+ forms.add(raw);
2818
+ forms.add(raw.toLowerCase());
2819
+ }
2820
+ for (const form of forms) {
2821
+ const fl = form.toLowerCase();
2822
+ if (EXACT_SENSITIVE_STRING_KEYS_LOWER.has(fl)) return true;
2823
+ for (const suffix of SENSITIVE_KEY_SUFFIXES_LOWER) if (fl.endsWith(suffix)) return true;
2824
+ }
2825
+ return false;
2826
+ }
2764
2827
  function isPlainSerializable(value) {
2765
2828
  if (value === null || typeof value !== "object") return true;
2766
2829
  if (Array.isArray(value)) return true;
@@ -2769,22 +2832,28 @@ function isPlainSerializable(value) {
2769
2832
  if (constructor && constructor.name !== "Object" && constructor.name !== "Array") return false;
2770
2833
  return true;
2771
2834
  }
2772
- function sanitizePluginOptions(pluginId, options, visiting = /* @__PURE__ */ new WeakSet()) {
2773
- if (options === null || options === void 0) return options;
2774
- if (typeof options === "function") return void 0;
2775
- if (typeof options !== "object") return options;
2776
- const obj = options;
2835
+ /**
2836
+ * Sanitize any plain JSON-like subtree from Better Auth options
2837
+ */
2838
+ function sanitizeConfig(value, visiting, excludeKeys) {
2839
+ if (value === null || value === void 0) return value;
2840
+ if (typeof value === "function") return void 0;
2841
+ if (typeof value !== "object") return value;
2842
+ const obj = value;
2777
2843
  if (visiting.has(obj)) return void 0;
2778
2844
  visiting.add(obj);
2779
2845
  try {
2780
- const excludeKeys = PLUGIN_OPTIONS_EXCLUDE_KEYS[pluginId];
2781
- if (Array.isArray(options)) return options.map((item) => sanitizePluginOptions(pluginId, item, visiting)).filter((item) => item !== void 0);
2846
+ if (Array.isArray(value)) return value.map((item) => sanitizeConfig(item, visiting, excludeKeys)).filter((item) => item !== void 0);
2782
2847
  const result = {};
2783
- for (const [key, value] of Object.entries(options)) {
2848
+ for (const [key, val] of Object.entries(value)) {
2784
2849
  if (excludeKeys?.has(key)) continue;
2785
- if (typeof value === "function") continue;
2786
- if (value !== null && typeof value === "object" && !isPlainSerializable(value)) continue;
2787
- const sanitized = sanitizePluginOptions(pluginId, value, visiting);
2850
+ if (typeof val === "function") continue;
2851
+ if (typeof val === "string" && shouldRedactSensitiveStringKey(key)) {
2852
+ result[key] = REDACTED_SIMPLE_STRING;
2853
+ continue;
2854
+ }
2855
+ if (val !== null && typeof val === "object" && !isPlainSerializable(val)) continue;
2856
+ const sanitized = sanitizeConfig(val, visiting, excludeKeys);
2788
2857
  if (sanitized !== void 0) result[key] = sanitized;
2789
2858
  }
2790
2859
  return result;
@@ -2792,6 +2861,9 @@ function sanitizePluginOptions(pluginId, options, visiting = /* @__PURE__ */ new
2792
2861
  visiting.delete(obj);
2793
2862
  }
2794
2863
  }
2864
+ function sanitizePluginOptions(pluginId, options) {
2865
+ return sanitizeConfig(options, /* @__PURE__ */ new WeakSet(), PLUGIN_OPTIONS_EXCLUDE_KEYS[pluginId]);
2866
+ }
2795
2867
  function estimateEntropy(str) {
2796
2868
  const unique = new Set(str).size;
2797
2869
  if (unique === 0) return 0;
@@ -2807,7 +2879,7 @@ const getConfig = (options) => {
2807
2879
  return {
2808
2880
  version: ctx.context.version || null,
2809
2881
  socialProviders: Object.keys(ctx.context.options.socialProviders || {}),
2810
- emailAndPassword: ctx.context.options.emailAndPassword,
2882
+ emailAndPassword: sanitizeConfig(ctx.context.options.emailAndPassword, /* @__PURE__ */ new WeakSet(), void 0),
2811
2883
  plugins: ctx.context.options.plugins?.map((plugin) => {
2812
2884
  const base = {
2813
2885
  id: plugin.id,
@@ -5424,11 +5496,14 @@ function getSSOPlugin(ctx) {
5424
5496
  const DEFAULT_MAX_SAML_METADATA_SIZE = 100 * 1024;
5425
5497
  const RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
5426
5498
  const SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1";
5499
+ /** @public */
5427
5500
  const DEPRECATED_SIGNATURE_ALGORITHMS = [RSA_SHA1];
5501
+ /** @public */
5428
5502
  const DEPRECATED_DIGEST_ALGORITHMS = [SHA1];
5429
5503
  function validateSAMLMetadataSize(metadataXml, maxSize = DEFAULT_MAX_SAML_METADATA_SIZE) {
5430
5504
  if (new TextEncoder().encode(metadataXml).byteLength > maxSize) throw new Error(`IdP metadata exceeds maximum allowed size (${Math.round(maxSize / 1024)}KB)`);
5431
5505
  }
5506
+ /** @public */
5432
5507
  function extractAlgorithmsFromSAMLMetadata(metadataXml) {
5433
5508
  const signatureAlgorithms = [];
5434
5509
  const digestAlgorithms = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/infra",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Dashboard and analytics plugin for Better Auth",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -68,9 +68,9 @@
68
68
  "@types/node": "catalog:",
69
69
  "better-auth": "catalog:",
70
70
  "expo-crypto": "^14.0.2",
71
- "happy-dom": "^20.8.9",
72
- "msw": "^2.13.0",
73
- "tsdown": "^0.21.8",
71
+ "happy-dom": "^20.9.0",
72
+ "msw": "^2.13.3",
73
+ "tsdown": "^0.21.9",
74
74
  "typescript": "catalog:",
75
75
  "zod": "catalog:"
76
76
  },
@@ -92,6 +92,9 @@
92
92
  "expo-device": ">=6.0.0"
93
93
  },
94
94
  "peerDependenciesMeta": {
95
+ "react-native": {
96
+ "optional": true
97
+ },
95
98
  "@react-native-async-storage/async-storage": {
96
99
  "optional": true
97
100
  },