@better-auth/infra 0.1.11 → 0.1.12

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.mjs CHANGED
@@ -815,7 +815,10 @@ const getOrganizationTriggerInfo = (user) => {
815
815
  const initTrackEvents = (options) => {
816
816
  const $fetch = createFetch({
817
817
  baseURL: options.apiUrl,
818
- headers: { "x-api-key": options.apiKey }
818
+ headers: {
819
+ "user-agent": "better-auth",
820
+ "x-api-key": options.apiKey
821
+ }
819
822
  });
820
823
  const trackEvent = (data) => {
821
824
  const track = async () => {
@@ -2678,13 +2681,16 @@ function isRecentlyIssued(payload) {
2678
2681
  }
2679
2682
  const jwtMiddleware = (options, schema, getJWT) => createAuthMiddleware(async (ctx) => {
2680
2683
  const jwsFromHeader = getJWT ? await getJWT(ctx) : ctx.headers?.get("Authorization")?.split(" ")[1];
2681
- if (!jwsFromHeader) throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2684
+ if (!jwsFromHeader) {
2685
+ ctx.context.logger.warn("[Dash] JWT is missing from header");
2686
+ throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2687
+ }
2682
2688
  const { payload } = await jwtVerify(jwsFromHeader, await getJWKs(options.apiUrl), { maxTokenAge: "5m" }).catch((e) => {
2683
- ctx.context.logger.error("[Dash] JWT verification failed:", e);
2689
+ ctx.context.logger.warn("[Dash] JWT verification failed:", e);
2684
2690
  throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2685
2691
  });
2686
2692
  if (!isRecentlyIssued(payload)) {
2687
- if (!(await betterFetch("/api/auth/check-jti", {
2693
+ const { error, data } = await betterFetch("/api/auth/check-jti", {
2688
2694
  baseURL: options.apiUrl,
2689
2695
  method: "POST",
2690
2696
  headers: { "x-api-key": options.apiKey },
@@ -2692,18 +2698,52 @@ const jwtMiddleware = (options, schema, getJWT) => createAuthMiddleware(async (c
2692
2698
  jti: payload.jti,
2693
2699
  expiresAt: payload.exp
2694
2700
  }
2695
- })).data?.valid) throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2701
+ });
2702
+ if (error || !data?.valid) {
2703
+ ctx.context.logger.warn("[Dash] JTI check failed with error", error, data?.valid);
2704
+ throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2705
+ }
2696
2706
  }
2697
2707
  const apiKeyHash = payload.apiKeyHash;
2698
- if (typeof apiKeyHash !== "string" || !options.apiKey) throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2699
- if (apiKeyHash !== await hash(options.apiKey)) throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2708
+ if (typeof apiKeyHash !== "string" || !options.apiKey) {
2709
+ ctx.context.logger.warn("[Dash] API key hash is missing or invalid", {
2710
+ apiKeyHash,
2711
+ apiKey: options.apiKey ? "present" : "missing"
2712
+ });
2713
+ throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2714
+ }
2715
+ const expectedHash = await hash(options.apiKey);
2716
+ if (apiKeyHash !== expectedHash) {
2717
+ ctx.context.logger.warn("[Dash] API key hash is invalid", apiKeyHash, expectedHash);
2718
+ throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2719
+ }
2700
2720
  if (schema) {
2701
2721
  const parsed = schema.safeParse(payload);
2702
- if (!parsed.success) throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2722
+ if (!parsed.success) {
2723
+ ctx.context.logger.warn("[Dash] JWT payload is invalid", parsed.error);
2724
+ throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2725
+ }
2703
2726
  return { payload: parsed.data };
2704
2727
  }
2705
2728
  return { payload };
2706
2729
  });
2730
+ /**
2731
+ * Lightweight JWT middleware for /dash/validate. Verifies JWT signature and
2732
+ * apiKeyHash only—no JTI check. Used during onboarding when the org doesn't
2733
+ * exist yet.
2734
+ */
2735
+ const jwtValidateMiddleware = (options) => createAuthMiddleware(async (ctx) => {
2736
+ const jwsFromHeader = ctx.headers?.get("Authorization")?.split(" ")[1];
2737
+ if (!jwsFromHeader) throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2738
+ const { payload } = await jwtVerify(jwsFromHeader, await getJWKs(options.apiUrl), { maxTokenAge: "5m" }).catch((e) => {
2739
+ ctx.context.logger.error("[Dash] JWT verification failed:", e);
2740
+ throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2741
+ });
2742
+ const apiKeyHash = payload.apiKeyHash;
2743
+ if (typeof apiKeyHash !== "string" || !options.apiKey) throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2744
+ if (apiKeyHash !== await hash(options.apiKey)) throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
2745
+ return { payload };
2746
+ });
2707
2747
 
2708
2748
  //#endregion
2709
2749
  //#region src/routes/config.ts
@@ -7419,6 +7459,20 @@ const generateBackupCodes = (options) => createAuthEndpoint("/dash/generate-back
7419
7459
  return { backupCodes: newBackupCodes };
7420
7460
  });
7421
7461
 
7462
+ //#endregion
7463
+ //#region src/routes/validate.ts
7464
+ /**
7465
+ * Lightweight endpoint to verify API key ownership during onboarding
7466
+ */
7467
+ const getValidate = (options) => {
7468
+ return createAuthEndpoint("/dash/validate", {
7469
+ method: "GET",
7470
+ use: [jwtValidateMiddleware(options)]
7471
+ }, async () => {
7472
+ return { valid: true };
7473
+ });
7474
+ };
7475
+
7422
7476
  //#endregion
7423
7477
  //#region src/pow.ts
7424
7478
  /** Default difficulty in bits (18 = ~500ms solve time) */
@@ -7922,6 +7976,7 @@ const dash = (options) => {
7922
7976
  },
7923
7977
  endpoints: {
7924
7978
  getDashConfig: getConfig(opts),
7979
+ getDashValidate: getValidate(opts),
7925
7980
  getDashUsers: getUsers(opts),
7926
7981
  exportDashUsers: exportUsers(opts),
7927
7982
  getOnlineUsersCount: getOnlineUsersCount(opts),
package/package.json CHANGED
@@ -1,12 +1,19 @@
1
1
  {
2
2
  "name": "@better-auth/infra",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "Dashboard and analytics plugin for Better Auth",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
7
7
  "module": "dist/index.mjs",
8
8
  "types": "dist/index.d.mts",
9
9
  "sideEffects": false,
10
+ "scripts": {
11
+ "build": "tsdown ",
12
+ "dev": "tsdown --watch",
13
+ "typecheck": "tsc --noEmit",
14
+ "test": "bun test",
15
+ "test:watch": "bun test --watch"
16
+ },
10
17
  "exports": {
11
18
  ".": {
12
19
  "types": "./dist/index.d.mts",
@@ -49,11 +56,11 @@
49
56
  "homepage": "https://better-auth.com",
50
57
  "devDependencies": {
51
58
  "@types/bun": "latest",
52
- "@types/node": "^24.12.0",
59
+ "@types/node": "catalog:",
53
60
  "better-auth": "beta",
54
61
  "@better-auth/scim": "beta",
55
62
  "tsdown": "^0.19.0-beta.2",
56
- "typescript": "^5.9.2",
63
+ "typescript": "catalog:",
57
64
  "zod": "beta"
58
65
  },
59
66
  "dependencies": {
@@ -67,12 +74,5 @@
67
74
  "zod": ">=4.1.12",
68
75
  "@better-auth/core": ">=1.4.0",
69
76
  "@better-auth/sso": ">=1.4.0"
70
- },
71
- "scripts": {
72
- "build": "tsdown ",
73
- "dev": "tsdown --watch",
74
- "typecheck": "tsc --noEmit",
75
- "test": "bun test",
76
- "test:watch": "bun test --watch"
77
77
  }
78
- }
78
+ }