better-auth 1.5.4 → 1.5.6

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.
Files changed (117) hide show
  1. package/dist/adapters/index.d.mts +25 -1
  2. package/dist/adapters/index.mjs +9 -1
  3. package/dist/adapters/index.mjs.map +1 -0
  4. package/dist/api/index.d.mts +36 -10
  5. package/dist/api/index.mjs +19 -4
  6. package/dist/api/index.mjs.map +1 -1
  7. package/dist/api/middlewares/origin-check.mjs +17 -8
  8. package/dist/api/middlewares/origin-check.mjs.map +1 -1
  9. package/dist/api/routes/account.d.mts +1 -1
  10. package/dist/api/routes/email-verification.d.mts +0 -1
  11. package/dist/api/routes/password.d.mts +1 -0
  12. package/dist/api/routes/password.mjs +2 -1
  13. package/dist/api/routes/password.mjs.map +1 -1
  14. package/dist/api/routes/session.d.mts +0 -1
  15. package/dist/api/routes/sign-in.d.mts +16 -2
  16. package/dist/api/routes/sign-in.mjs +10 -2
  17. package/dist/api/routes/sign-in.mjs.map +1 -1
  18. package/dist/api/routes/sign-up.d.mts +0 -1
  19. package/dist/api/routes/sign-up.mjs +3 -2
  20. package/dist/api/routes/sign-up.mjs.map +1 -1
  21. package/dist/api/routes/update-session.d.mts +0 -1
  22. package/dist/api/routes/update-user.d.mts +0 -1
  23. package/dist/api/to-auth-endpoints.mjs +49 -12
  24. package/dist/api/to-auth-endpoints.mjs.map +1 -1
  25. package/dist/auth/full.d.mts +0 -1
  26. package/dist/auth/minimal.d.mts +0 -1
  27. package/dist/client/index.d.mts +3 -4
  28. package/dist/client/index.mjs.map +1 -1
  29. package/dist/client/path-to-object.d.mts +9 -2
  30. package/dist/client/query.mjs +3 -2
  31. package/dist/client/query.mjs.map +1 -1
  32. package/dist/client/session-refresh.d.mts +11 -3
  33. package/dist/client/session-refresh.mjs +13 -8
  34. package/dist/client/session-refresh.mjs.map +1 -1
  35. package/dist/client/types.d.mts +0 -1
  36. package/dist/context/create-context.mjs +4 -1
  37. package/dist/context/create-context.mjs.map +1 -1
  38. package/dist/context/helpers.mjs +10 -4
  39. package/dist/context/helpers.mjs.map +1 -1
  40. package/dist/cookies/index.d.mts +0 -1
  41. package/dist/cookies/session-store.d.mts +0 -2
  42. package/dist/db/get-migration.mjs +3 -2
  43. package/dist/db/get-migration.mjs.map +1 -1
  44. package/dist/db/index.d.mts +2 -2
  45. package/dist/db/internal-adapter.d.mts +2 -1
  46. package/dist/db/internal-adapter.mjs +1 -1
  47. package/dist/db/internal-adapter.mjs.map +1 -1
  48. package/dist/db/schema.d.mts +0 -1
  49. package/dist/db/with-hooks.d.mts +6 -2
  50. package/dist/db/with-hooks.mjs +72 -31
  51. package/dist/db/with-hooks.mjs.map +1 -1
  52. package/dist/index.d.mts +0 -2
  53. package/dist/integrations/node.d.mts +0 -1
  54. package/dist/oauth2/link-account.d.mts +0 -1
  55. package/dist/plugins/admin/access/statement.d.mts +0 -2
  56. package/dist/plugins/admin/admin.d.mts +0 -1
  57. package/dist/plugins/admin/client.d.mts +0 -2
  58. package/dist/plugins/admin/types.d.mts +0 -2
  59. package/dist/plugins/anonymous/types.d.mts +0 -1
  60. package/dist/plugins/email-otp/index.mjs +2 -1
  61. package/dist/plugins/email-otp/index.mjs.map +1 -1
  62. package/dist/plugins/email-otp/otp-token.mjs +31 -2
  63. package/dist/plugins/email-otp/otp-token.mjs.map +1 -1
  64. package/dist/plugins/email-otp/routes.mjs +60 -59
  65. package/dist/plugins/email-otp/routes.mjs.map +1 -1
  66. package/dist/plugins/email-otp/types.d.mts +12 -0
  67. package/dist/plugins/email-otp/utils.mjs +4 -1
  68. package/dist/plugins/email-otp/utils.mjs.map +1 -1
  69. package/dist/plugins/generic-oauth/client.d.mts +0 -1
  70. package/dist/plugins/generic-oauth/index.d.mts +0 -1
  71. package/dist/plugins/index.d.mts +0 -3
  72. package/dist/plugins/jwt/types.d.mts +0 -1
  73. package/dist/plugins/magic-link/index.d.mts +2 -0
  74. package/dist/plugins/magic-link/index.mjs +5 -3
  75. package/dist/plugins/magic-link/index.mjs.map +1 -1
  76. package/dist/plugins/mcp/index.d.mts +0 -1
  77. package/dist/plugins/oidc-provider/authorize.mjs +13 -4
  78. package/dist/plugins/oidc-provider/authorize.mjs.map +1 -1
  79. package/dist/plugins/oidc-provider/error.mjs +12 -2
  80. package/dist/plugins/oidc-provider/error.mjs.map +1 -1
  81. package/dist/plugins/oidc-provider/index.d.mts +0 -1
  82. package/dist/plugins/oidc-provider/types.d.mts +0 -1
  83. package/dist/plugins/one-time-token/index.d.mts +0 -1
  84. package/dist/plugins/organization/access/statement.d.mts +0 -2
  85. package/dist/plugins/organization/adapter.d.mts +0 -2
  86. package/dist/plugins/organization/adapter.mjs +2 -2
  87. package/dist/plugins/organization/adapter.mjs.map +1 -1
  88. package/dist/plugins/organization/client.d.mts +0 -5
  89. package/dist/plugins/organization/organization.d.mts +0 -2
  90. package/dist/plugins/organization/permission.d.mts +0 -1
  91. package/dist/plugins/organization/routes/crud-access-control.d.mts +0 -2
  92. package/dist/plugins/organization/routes/crud-invites.d.mts +0 -3
  93. package/dist/plugins/organization/routes/crud-invites.mjs +1 -1
  94. package/dist/plugins/organization/routes/crud-invites.mjs.map +1 -1
  95. package/dist/plugins/organization/routes/crud-members.d.mts +0 -3
  96. package/dist/plugins/organization/routes/crud-members.mjs +1 -1
  97. package/dist/plugins/organization/routes/crud-members.mjs.map +1 -1
  98. package/dist/plugins/organization/routes/crud-org.d.mts +0 -3
  99. package/dist/plugins/organization/routes/crud-team.d.mts +2 -3
  100. package/dist/plugins/organization/routes/crud-team.mjs +18 -14
  101. package/dist/plugins/organization/routes/crud-team.mjs.map +1 -1
  102. package/dist/plugins/organization/schema.d.mts +0 -1
  103. package/dist/plugins/organization/types.d.mts +0 -2
  104. package/dist/plugins/phone-number/types.d.mts +0 -1
  105. package/dist/plugins/siwe/index.d.mts +0 -1
  106. package/dist/plugins/test-utils/types.d.mts +0 -2
  107. package/dist/plugins/two-factor/client.d.mts +7 -0
  108. package/dist/plugins/two-factor/client.mjs +5 -1
  109. package/dist/plugins/two-factor/client.mjs.map +1 -1
  110. package/dist/plugins/two-factor/index.mjs +7 -1
  111. package/dist/plugins/two-factor/index.mjs.map +1 -1
  112. package/dist/plugins/two-factor/otp/index.d.mts +2 -2
  113. package/dist/plugins/two-factor/otp/index.mjs.map +1 -1
  114. package/dist/plugins/two-factor/types.d.mts +7 -1
  115. package/dist/test-utils/test-instance.d.mts +108 -21
  116. package/dist/types/index.d.mts +0 -1
  117. package/package.json +13 -10
@@ -1,3 +1,27 @@
1
+ import * as _better_auth_core0 from "@better-auth/core";
1
2
  import { AdapterFactory, AdapterFactoryConfig, AdapterFactoryCustomizeAdapterCreator, AdapterFactoryOptions, AdapterTestDebugLogs, CustomAdapter, createAdapterFactory, initGetDefaultFieldName, initGetDefaultModelName, initGetFieldAttributes, initGetFieldName, initGetIdField, initGetModelName } from "@better-auth/core/db/adapter";
2
3
  export * from "@better-auth/core/db/adapter";
3
- export { type AdapterFactory, type AdapterFactoryConfig, type AdapterFactoryCustomizeAdapterCreator, type AdapterFactoryOptions, type AdapterTestDebugLogs, type CustomAdapter, createAdapterFactory, initGetDefaultFieldName, initGetDefaultModelName, initGetFieldAttributes, initGetFieldName, initGetIdField, initGetModelName };
4
+
5
+ //#region src/adapters/index.d.ts
6
+ /**
7
+ * @deprecated Use `createAdapterFactory` instead.
8
+ */
9
+ declare const createAdapter: <Options extends _better_auth_core0.BetterAuthOptions>({
10
+ adapter: customAdapter,
11
+ config: cfg
12
+ }: AdapterFactoryOptions) => AdapterFactory<Options>;
13
+ /**
14
+ * @deprecated Use `AdapterFactoryOptions` instead.
15
+ */
16
+ type CreateAdapterOptions = AdapterFactoryOptions;
17
+ /**
18
+ * @deprecated Use `AdapterFactoryConfig` instead.
19
+ */
20
+ type AdapterConfig = AdapterFactoryConfig;
21
+ /**
22
+ * @deprecated Use `AdapterFactoryCustomizeAdapterCreator` instead.
23
+ */
24
+ type CreateCustomAdapter = AdapterFactoryCustomizeAdapterCreator;
25
+ //#endregion
26
+ export { AdapterConfig, type AdapterFactory, type AdapterFactoryConfig, type AdapterFactoryCustomizeAdapterCreator, type AdapterFactoryOptions, type AdapterTestDebugLogs, CreateAdapterOptions, CreateCustomAdapter, type CustomAdapter, createAdapter, createAdapterFactory, initGetDefaultFieldName, initGetDefaultModelName, initGetFieldAttributes, initGetFieldName, initGetIdField, initGetModelName };
27
+ //# sourceMappingURL=index.d.mts.map
@@ -2,4 +2,12 @@ import { createAdapterFactory, initGetDefaultFieldName, initGetDefaultModelName,
2
2
 
3
3
  export * from "@better-auth/core/db/adapter"
4
4
 
5
- export { createAdapterFactory, initGetDefaultFieldName, initGetDefaultModelName, initGetFieldAttributes, initGetFieldName, initGetIdField, initGetModelName };
5
+ //#region src/adapters/index.ts
6
+ /**
7
+ * @deprecated Use `createAdapterFactory` instead.
8
+ */
9
+ const createAdapter = createAdapterFactory;
10
+
11
+ //#endregion
12
+ export { createAdapter, createAdapterFactory, initGetDefaultFieldName, initGetDefaultModelName, initGetFieldAttributes, initGetFieldName, initGetIdField, initGetModelName };
13
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/adapters/index.ts"],"sourcesContent":["import type {\n\tAdapterFactory,\n\tAdapterFactoryConfig,\n\tAdapterFactoryCustomizeAdapterCreator,\n\tAdapterFactoryOptions,\n\tAdapterTestDebugLogs,\n\tCustomAdapter,\n} from \"@better-auth/core/db/adapter\";\nimport {\n\tcreateAdapterFactory,\n\tinitGetDefaultFieldName,\n\tinitGetDefaultModelName,\n\tinitGetFieldAttributes,\n\tinitGetFieldName,\n\tinitGetIdField,\n\tinitGetModelName,\n} from \"@better-auth/core/db/adapter\";\n\nexport * from \"@better-auth/core/db/adapter\";\n\nexport type {\n\tAdapterFactoryOptions,\n\tAdapterFactory,\n\tAdapterTestDebugLogs,\n\tAdapterFactoryConfig,\n\tCustomAdapter,\n\tAdapterFactoryCustomizeAdapterCreator,\n};\nexport {\n\tcreateAdapterFactory,\n\tinitGetDefaultFieldName,\n\tinitGetDefaultModelName,\n\tinitGetFieldName,\n\tinitGetModelName,\n\tinitGetFieldAttributes,\n\tinitGetIdField,\n};\n\n/**\n * @deprecated Use `createAdapterFactory` instead.\n */\nexport const createAdapter = createAdapterFactory;\n\n/**\n * @deprecated Use `AdapterFactoryOptions` instead.\n */\nexport type CreateAdapterOptions = AdapterFactoryOptions;\n\n/**\n * @deprecated Use `AdapterFactoryConfig` instead.\n */\nexport type AdapterConfig = AdapterFactoryConfig;\n\n/**\n * @deprecated Use `AdapterFactoryCustomizeAdapterCreator` instead.\n */\nexport type CreateCustomAdapter = AdapterFactoryCustomizeAdapterCreator;\n"],"mappings":";;;;;;;;AAyCA,MAAa,gBAAgB"}
@@ -1,11 +1,8 @@
1
1
  import { Prettify as Prettify$1, UnionToIntersection } from "../types/helper.mjs";
2
2
  import { AdditionalSessionFieldsInput, AdditionalUserFieldsInput } from "../types/models.mjs";
3
- import "../types/index.mjs";
4
- import "../index.mjs";
5
3
  import { getIp } from "../utils/get-request-ip.mjs";
6
4
  import { isAPIError } from "../utils/is-api-error.mjs";
7
5
  import { formCsrfMiddleware, originCheck, originCheckMiddleware } from "./middlewares/origin-check.mjs";
8
- import "./middlewares/index.mjs";
9
6
  import { accountInfo, getAccessToken, linkSocialAccount, listUserAccounts, refreshToken, unlinkAccount } from "./routes/account.mjs";
10
7
  import { callbackOAuth } from "./routes/callback.mjs";
11
8
  import { createEmailVerificationToken, sendVerificationEmail, sendVerificationEmailFn, verifyEmail } from "./routes/email-verification.mjs";
@@ -18,7 +15,6 @@ import { signOut } from "./routes/sign-out.mjs";
18
15
  import { signUpEmail } from "./routes/sign-up.mjs";
19
16
  import { updateSession } from "./routes/update-session.mjs";
20
17
  import { changeEmail, changePassword, deleteUser, deleteUserCallback, setPassword, updateUser } from "./routes/update-user.mjs";
21
- import "./routes/index.mjs";
22
18
  import { getOAuthState } from "./state/oauth.mjs";
23
19
  import { getShouldSkipSessionRefresh, setShouldSkipSessionRefresh } from "./state/should-session-refresh.mjs";
24
20
  import { AuthContext, Awaitable, BetterAuthOptions, BetterAuthPlugin } from "@better-auth/core";
@@ -94,7 +90,7 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
94
90
  callbackURL: zod.ZodOptional<zod.ZodString>;
95
91
  newUserCallbackURL: zod.ZodOptional<zod.ZodString>;
96
92
  errorCallbackURL: zod.ZodOptional<zod.ZodString>;
97
- provider: zod.ZodType<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel", unknown, zod_v4_core0.$ZodTypeInternals<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel", unknown>>;
93
+ provider: zod.ZodType<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat", unknown, zod_v4_core0.$ZodTypeInternals<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat", unknown>>;
98
94
  disableRedirect: zod.ZodOptional<zod.ZodBoolean>;
99
95
  idToken: zod.ZodOptional<zod.ZodObject<{
100
96
  token: zod.ZodString;
@@ -102,6 +98,13 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
102
98
  accessToken: zod.ZodOptional<zod.ZodString>;
103
99
  refreshToken: zod.ZodOptional<zod.ZodString>;
104
100
  expiresAt: zod.ZodOptional<zod.ZodNumber>;
101
+ user: zod.ZodOptional<zod.ZodObject<{
102
+ name: zod.ZodOptional<zod.ZodObject<{
103
+ firstName: zod.ZodOptional<zod.ZodString>;
104
+ lastName: zod.ZodOptional<zod.ZodString>;
105
+ }, zod_v4_core0.$strip>>;
106
+ email: zod.ZodOptional<zod.ZodString>;
107
+ }, zod_v4_core0.$strip>>;
105
108
  }, zod_v4_core0.$strip>>;
106
109
  scopes: zod.ZodOptional<zod.ZodArray<zod.ZodString>>;
107
110
  requestSignUp: zod.ZodOptional<zod.ZodBoolean>;
@@ -114,7 +117,7 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
114
117
  callbackURL: zod.ZodOptional<zod.ZodString>;
115
118
  newUserCallbackURL: zod.ZodOptional<zod.ZodString>;
116
119
  errorCallbackURL: zod.ZodOptional<zod.ZodString>;
117
- provider: zod.ZodType<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel", unknown, zod_v4_core0.$ZodTypeInternals<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel", unknown>>;
120
+ provider: zod.ZodType<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat", unknown, zod_v4_core0.$ZodTypeInternals<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat", unknown>>;
118
121
  disableRedirect: zod.ZodOptional<zod.ZodBoolean>;
119
122
  idToken: zod.ZodOptional<zod.ZodObject<{
120
123
  token: zod.ZodString;
@@ -122,6 +125,13 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
122
125
  accessToken: zod.ZodOptional<zod.ZodString>;
123
126
  refreshToken: zod.ZodOptional<zod.ZodString>;
124
127
  expiresAt: zod.ZodOptional<zod.ZodNumber>;
128
+ user: zod.ZodOptional<zod.ZodObject<{
129
+ name: zod.ZodOptional<zod.ZodObject<{
130
+ firstName: zod.ZodOptional<zod.ZodString>;
131
+ lastName: zod.ZodOptional<zod.ZodString>;
132
+ }, zod_v4_core0.$strip>>;
133
+ email: zod.ZodOptional<zod.ZodString>;
134
+ }, zod_v4_core0.$strip>>;
125
135
  }, zod_v4_core0.$strip>>;
126
136
  scopes: zod.ZodOptional<zod.ZodArray<zod.ZodString>>;
127
137
  requestSignUp: zod.ZodOptional<zod.ZodBoolean>;
@@ -1265,6 +1275,7 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
1265
1275
  };
1266
1276
  };
1267
1277
  };
1278
+ use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>)[];
1268
1279
  }, {
1269
1280
  status: boolean;
1270
1281
  message: string;
@@ -1558,7 +1569,7 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
1558
1569
  requireHeaders: true;
1559
1570
  body: zod.ZodObject<{
1560
1571
  callbackURL: zod.ZodOptional<zod.ZodString>;
1561
- provider: zod.ZodType<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel", unknown, zod_v4_core0.$ZodTypeInternals<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel", unknown>>;
1572
+ provider: zod.ZodType<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat", unknown, zod_v4_core0.$ZodTypeInternals<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat", unknown>>;
1562
1573
  idToken: zod.ZodOptional<zod.ZodObject<{
1563
1574
  token: zod.ZodString;
1564
1575
  nonce: zod.ZodOptional<zod.ZodString>;
@@ -2065,7 +2076,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2065
2076
  callbackURL: zod.ZodOptional<zod.ZodString>;
2066
2077
  newUserCallbackURL: zod.ZodOptional<zod.ZodString>;
2067
2078
  errorCallbackURL: zod.ZodOptional<zod.ZodString>;
2068
- provider: zod.ZodType<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel", unknown, zod_v4_core0.$ZodTypeInternals<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel", unknown>>;
2079
+ provider: zod.ZodType<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat", unknown, zod_v4_core0.$ZodTypeInternals<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat", unknown>>;
2069
2080
  disableRedirect: zod.ZodOptional<zod.ZodBoolean>;
2070
2081
  idToken: zod.ZodOptional<zod.ZodObject<{
2071
2082
  token: zod.ZodString;
@@ -2073,6 +2084,13 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2073
2084
  accessToken: zod.ZodOptional<zod.ZodString>;
2074
2085
  refreshToken: zod.ZodOptional<zod.ZodString>;
2075
2086
  expiresAt: zod.ZodOptional<zod.ZodNumber>;
2087
+ user: zod.ZodOptional<zod.ZodObject<{
2088
+ name: zod.ZodOptional<zod.ZodObject<{
2089
+ firstName: zod.ZodOptional<zod.ZodString>;
2090
+ lastName: zod.ZodOptional<zod.ZodString>;
2091
+ }, zod_v4_core0.$strip>>;
2092
+ email: zod.ZodOptional<zod.ZodString>;
2093
+ }, zod_v4_core0.$strip>>;
2076
2094
  }, zod_v4_core0.$strip>>;
2077
2095
  scopes: zod.ZodOptional<zod.ZodArray<zod.ZodString>>;
2078
2096
  requestSignUp: zod.ZodOptional<zod.ZodBoolean>;
@@ -2085,7 +2103,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2085
2103
  callbackURL: zod.ZodOptional<zod.ZodString>;
2086
2104
  newUserCallbackURL: zod.ZodOptional<zod.ZodString>;
2087
2105
  errorCallbackURL: zod.ZodOptional<zod.ZodString>;
2088
- provider: zod.ZodType<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel", unknown, zod_v4_core0.$ZodTypeInternals<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel", unknown>>;
2106
+ provider: zod.ZodType<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat", unknown, zod_v4_core0.$ZodTypeInternals<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat", unknown>>;
2089
2107
  disableRedirect: zod.ZodOptional<zod.ZodBoolean>;
2090
2108
  idToken: zod.ZodOptional<zod.ZodObject<{
2091
2109
  token: zod.ZodString;
@@ -2093,6 +2111,13 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2093
2111
  accessToken: zod.ZodOptional<zod.ZodString>;
2094
2112
  refreshToken: zod.ZodOptional<zod.ZodString>;
2095
2113
  expiresAt: zod.ZodOptional<zod.ZodNumber>;
2114
+ user: zod.ZodOptional<zod.ZodObject<{
2115
+ name: zod.ZodOptional<zod.ZodObject<{
2116
+ firstName: zod.ZodOptional<zod.ZodString>;
2117
+ lastName: zod.ZodOptional<zod.ZodString>;
2118
+ }, zod_v4_core0.$strip>>;
2119
+ email: zod.ZodOptional<zod.ZodString>;
2120
+ }, zod_v4_core0.$strip>>;
2096
2121
  }, zod_v4_core0.$strip>>;
2097
2122
  scopes: zod.ZodOptional<zod.ZodArray<zod.ZodString>>;
2098
2123
  requestSignUp: zod.ZodOptional<zod.ZodBoolean>;
@@ -3236,6 +3261,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
3236
3261
  };
3237
3262
  };
3238
3263
  };
3264
+ use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>)[];
3239
3265
  }, {
3240
3266
  status: boolean;
3241
3267
  message: string;
@@ -3529,7 +3555,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
3529
3555
  requireHeaders: true;
3530
3556
  body: zod.ZodObject<{
3531
3557
  callbackURL: zod.ZodOptional<zod.ZodString>;
3532
- provider: zod.ZodType<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel", unknown, zod_v4_core0.$ZodTypeInternals<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel", unknown>>;
3558
+ provider: zod.ZodType<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat", unknown, zod_v4_core0.$ZodTypeInternals<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat", unknown>>;
3533
3559
  idToken: zod.ZodOptional<zod.ZodObject<{
3534
3560
  token: zod.ZodString;
3535
3561
  nonce: zod.ZodOptional<zod.ZodString>;
@@ -21,6 +21,7 @@ import "./routes/index.mjs";
21
21
  import { toAuthEndpoints } from "./to-auth-endpoints.mjs";
22
22
  import { logger } from "@better-auth/core/env";
23
23
  import { APIError } from "@better-auth/core/error";
24
+ import { ATTR_CONTEXT, ATTR_HOOK_TYPE, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_HTTP_ROUTE, withSpan } from "@better-auth/core/instrumentation";
24
25
  import { normalizePathname } from "@better-auth/core/utils/url";
25
26
  import { createRouter } from "better-call";
26
27
  import { createAuthEndpoint, createAuthMiddleware, optionsMiddleware } from "@better-auth/core/api";
@@ -91,13 +92,17 @@ function getEndpoints(ctx, options) {
91
92
  const middlewares = options.plugins?.map((plugin) => plugin.middlewares?.map((m) => {
92
93
  const middleware = (async (context) => {
93
94
  const authContext = await ctx;
94
- return m.middleware({
95
+ return withSpan(`middleware ${m.path} ${plugin.id}`, {
96
+ [ATTR_HOOK_TYPE]: "middleware",
97
+ [ATTR_HTTP_ROUTE]: m.path,
98
+ [ATTR_CONTEXT]: `plugin:${plugin.id}`
99
+ }, () => m.middleware({
95
100
  ...context,
96
101
  context: {
97
102
  ...authContext,
98
103
  ...context.context
99
104
  }
100
- });
105
+ }));
101
106
  });
102
107
  middleware.options = m.middleware.options;
103
108
  return {
@@ -162,7 +167,11 @@ const router = (ctx, options) => {
162
167
  if (disabledPaths.includes(normalizedPath)) return new Response("Not Found", { status: 404 });
163
168
  let currentRequest = req;
164
169
  for (const plugin of ctx.options.plugins || []) if (plugin.onRequest) {
165
- const response = await plugin.onRequest(currentRequest, ctx);
170
+ const response = await withSpan(`onRequest ${normalizedPath} ${plugin.id}`, {
171
+ [ATTR_HOOK_TYPE]: "onRequest",
172
+ [ATTR_CONTEXT]: `plugin:${plugin.id}`,
173
+ [ATTR_HTTP_ROUTE]: normalizedPath
174
+ }, () => plugin.onRequest(currentRequest, ctx));
166
175
  if (response && "response" in response) return response.response;
167
176
  if (response && "request" in response) currentRequest = response.request;
168
177
  }
@@ -172,8 +181,14 @@ const router = (ctx, options) => {
172
181
  },
173
182
  async onResponse(res, req) {
174
183
  await onResponseRateLimit(req, ctx);
184
+ const normalizedPath = normalizePathname(req.url, basePath);
175
185
  for (const plugin of ctx.options.plugins || []) if (plugin.onResponse) {
176
- const response = await plugin.onResponse(res, ctx);
186
+ const response = await withSpan(`onResponse ${normalizedPath} ${plugin.id}`, {
187
+ [ATTR_HOOK_TYPE]: "onResponse",
188
+ [ATTR_CONTEXT]: `plugin:${plugin.id}`,
189
+ [ATTR_HTTP_ROUTE]: normalizedPath,
190
+ [ATTR_HTTP_RESPONSE_STATUS_CODE]: res.status
191
+ }, () => plugin.onResponse(res, ctx));
177
192
  if (response) return response.response;
178
193
  }
179
194
  return res;
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/api/index.ts"],"sourcesContent":["import type {\n\tAuthContext,\n\tAwaitable,\n\tBetterAuthOptions,\n\tBetterAuthPlugin,\n} from \"@better-auth/core\";\nimport type { InternalLogger } from \"@better-auth/core/env\";\nimport { logger } from \"@better-auth/core/env\";\nimport { normalizePathname } from \"@better-auth/core/utils/url\";\nimport type { Endpoint, Middleware } from \"better-call\";\nimport { createRouter } from \"better-call\";\nimport type { UnionToIntersection } from \"../types\";\nimport { isAPIError } from \"../utils/is-api-error\";\nimport { originCheckMiddleware } from \"./middlewares\";\nimport { onRequestRateLimit, onResponseRateLimit } from \"./rate-limiter\";\nimport {\n\taccountInfo,\n\tcallbackOAuth,\n\tchangeEmail,\n\tchangePassword,\n\tdeleteUser,\n\tdeleteUserCallback,\n\terror,\n\tgetAccessToken,\n\tgetSession,\n\tlinkSocialAccount,\n\tlistSessions,\n\tlistUserAccounts,\n\tok,\n\trefreshToken,\n\trequestPasswordReset,\n\trequestPasswordResetCallback,\n\tresetPassword,\n\trevokeOtherSessions,\n\trevokeSession,\n\trevokeSessions,\n\tsendVerificationEmail,\n\tsetPassword,\n\tsignInEmail,\n\tsignInSocial,\n\tsignOut,\n\tsignUpEmail,\n\tunlinkAccount,\n\tupdateSession,\n\tupdateUser,\n\tverifyEmail,\n\tverifyPassword,\n} from \"./routes\";\nimport { toAuthEndpoints } from \"./to-auth-endpoints\";\n\nexport function checkEndpointConflicts(\n\toptions: BetterAuthOptions,\n\tlogger: InternalLogger,\n) {\n\tconst endpointRegistry = new Map<\n\t\tstring,\n\t\t{ pluginId: string; endpointKey: string; methods: string[] }[]\n\t>();\n\n\toptions.plugins?.forEach((plugin) => {\n\t\tif (plugin.endpoints) {\n\t\t\tfor (const [key, endpoint] of Object.entries(plugin.endpoints)) {\n\t\t\t\tif (\n\t\t\t\t\tendpoint &&\n\t\t\t\t\t\"path\" in endpoint &&\n\t\t\t\t\ttypeof endpoint.path === \"string\"\n\t\t\t\t) {\n\t\t\t\t\tconst path = endpoint.path;\n\t\t\t\t\tlet methods: string[] = [];\n\t\t\t\t\tif (endpoint.options && \"method\" in endpoint.options) {\n\t\t\t\t\t\tif (Array.isArray(endpoint.options.method)) {\n\t\t\t\t\t\t\tmethods = endpoint.options.method;\n\t\t\t\t\t\t} else if (typeof endpoint.options.method === \"string\") {\n\t\t\t\t\t\t\tmethods = [endpoint.options.method];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (methods.length === 0) {\n\t\t\t\t\t\tmethods = [\"*\"];\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!endpointRegistry.has(path)) {\n\t\t\t\t\t\tendpointRegistry.set(path, []);\n\t\t\t\t\t}\n\t\t\t\t\tendpointRegistry.get(path)!.push({\n\t\t\t\t\t\tpluginId: plugin.id,\n\t\t\t\t\t\tendpointKey: key,\n\t\t\t\t\t\tmethods,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\tconst conflicts: {\n\t\tpath: string;\n\t\tplugins: string[];\n\t\tconflictingMethods: string[];\n\t}[] = [];\n\tfor (const [path, entries] of endpointRegistry.entries()) {\n\t\tif (entries.length > 1) {\n\t\t\tconst methodMap = new Map<string, string[]>();\n\t\t\tlet hasConflict = false;\n\n\t\t\tfor (const entry of entries) {\n\t\t\t\tfor (const method of entry.methods) {\n\t\t\t\t\tif (!methodMap.has(method)) {\n\t\t\t\t\t\tmethodMap.set(method, []);\n\t\t\t\t\t}\n\t\t\t\t\tmethodMap.get(method)!.push(entry.pluginId);\n\n\t\t\t\t\tif (methodMap.get(method)!.length > 1) {\n\t\t\t\t\t\thasConflict = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (method === \"*\" && entries.length > 1) {\n\t\t\t\t\t\thasConflict = true;\n\t\t\t\t\t} else if (method !== \"*\" && methodMap.has(\"*\")) {\n\t\t\t\t\t\thasConflict = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (hasConflict) {\n\t\t\t\tconst uniquePlugins = [...new Set(entries.map((e) => e.pluginId))];\n\t\t\t\tconst conflictingMethods: string[] = [];\n\n\t\t\t\tfor (const [method, plugins] of methodMap.entries()) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tplugins.length > 1 ||\n\t\t\t\t\t\t(method === \"*\" && entries.length > 1) ||\n\t\t\t\t\t\t(method !== \"*\" && methodMap.has(\"*\"))\n\t\t\t\t\t) {\n\t\t\t\t\t\tconflictingMethods.push(method);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconflicts.push({\n\t\t\t\t\tpath,\n\t\t\t\t\tplugins: uniquePlugins,\n\t\t\t\t\tconflictingMethods,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tif (conflicts.length > 0) {\n\t\tconst conflictMessages = conflicts\n\t\t\t.map(\n\t\t\t\t(conflict) =>\n\t\t\t\t\t` - \"${conflict.path}\" [${conflict.conflictingMethods.join(\", \")}] used by plugins: ${conflict.plugins.join(\", \")}`,\n\t\t\t)\n\t\t\t.join(\"\\n\");\n\t\tlogger.error(\n\t\t\t`Endpoint path conflicts detected! Multiple plugins are trying to use the same endpoint paths with conflicting HTTP methods:\n${conflictMessages}\n\nTo resolve this, you can:\n\t1. Use only one of the conflicting plugins\n\t2. Configure the plugins to use different paths (if supported)\n\t3. Ensure plugins use different HTTP methods for the same path\n`,\n\t\t);\n\t}\n}\n\nexport function getEndpoints<Option extends BetterAuthOptions>(\n\tctx: Awaitable<AuthContext>,\n\toptions: Option,\n) {\n\tconst pluginEndpoints =\n\t\toptions.plugins?.reduce<Record<string, Endpoint>>((acc, plugin) => {\n\t\t\treturn {\n\t\t\t\t...acc,\n\t\t\t\t...plugin.endpoints,\n\t\t\t};\n\t\t}, {}) ?? {};\n\n\ttype PluginEndpoint = UnionToIntersection<\n\t\tOption[\"plugins\"] extends Array<infer T>\n\t\t\t? T extends BetterAuthPlugin\n\t\t\t\t? T extends {\n\t\t\t\t\t\tendpoints: infer E;\n\t\t\t\t\t}\n\t\t\t\t\t? E\n\t\t\t\t\t: {}\n\t\t\t\t: {}\n\t\t\t: {}\n\t>;\n\n\tconst middlewares =\n\t\toptions.plugins\n\t\t\t?.map((plugin) =>\n\t\t\t\tplugin.middlewares?.map((m) => {\n\t\t\t\t\tconst middleware = (async (context: any) => {\n\t\t\t\t\t\tconst authContext = await ctx;\n\t\t\t\t\t\treturn m.middleware({\n\t\t\t\t\t\t\t...context,\n\t\t\t\t\t\t\tcontext: {\n\t\t\t\t\t\t\t\t...authContext,\n\t\t\t\t\t\t\t\t...context.context,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}) as Middleware;\n\t\t\t\t\tmiddleware.options = m.middleware.options;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpath: m.path,\n\t\t\t\t\t\tmiddleware,\n\t\t\t\t\t};\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.filter((plugin) => plugin !== undefined)\n\t\t\t.flat() || [];\n\n\tconst baseEndpoints = {\n\t\tsignInSocial: signInSocial<Option>(),\n\t\tcallbackOAuth,\n\t\tgetSession: getSession<Option>(),\n\t\tsignOut,\n\t\tsignUpEmail: signUpEmail<Option>(),\n\t\tsignInEmail: signInEmail<Option>(),\n\t\tresetPassword,\n\t\tverifyPassword,\n\t\tverifyEmail,\n\t\tsendVerificationEmail,\n\t\tchangeEmail,\n\t\tchangePassword,\n\t\tsetPassword,\n\t\tupdateSession: updateSession<Option>(),\n\t\tupdateUser: updateUser<Option>(),\n\t\tdeleteUser,\n\t\trequestPasswordReset,\n\t\trequestPasswordResetCallback,\n\t\tlistSessions: listSessions<Option>(),\n\t\trevokeSession,\n\t\trevokeSessions,\n\t\trevokeOtherSessions,\n\t\tlinkSocialAccount,\n\t\tlistUserAccounts,\n\t\tdeleteUserCallback,\n\t\tunlinkAccount,\n\t\trefreshToken,\n\t\tgetAccessToken,\n\t\taccountInfo,\n\t};\n\tconst endpoints = {\n\t\t...baseEndpoints,\n\t\t...pluginEndpoints,\n\t\tok,\n\t\terror,\n\t} as const;\n\tconst api = toAuthEndpoints(endpoints, ctx);\n\treturn {\n\t\tapi: api as typeof endpoints & PluginEndpoint,\n\t\tmiddlewares,\n\t};\n}\nexport const router = <Option extends BetterAuthOptions>(\n\tctx: AuthContext,\n\toptions: Option,\n) => {\n\tconst { api, middlewares } = getEndpoints(ctx, options);\n\tconst basePath = new URL(ctx.baseURL).pathname;\n\n\treturn createRouter(api, {\n\t\trouterContext: ctx,\n\t\topenapi: {\n\t\t\tdisabled: true,\n\t\t},\n\t\tbasePath,\n\t\trouterMiddleware: [\n\t\t\t{\n\t\t\t\tpath: \"/**\",\n\t\t\t\tmiddleware: originCheckMiddleware,\n\t\t\t},\n\t\t\t...middlewares,\n\t\t],\n\t\tallowedMediaTypes: [\"application/json\"],\n\t\tskipTrailingSlashes: options.advanced?.skipTrailingSlashes ?? false,\n\t\tasync onRequest(req) {\n\t\t\t//handle disabled paths\n\t\t\tconst disabledPaths = ctx.options.disabledPaths || [];\n\t\t\tconst normalizedPath = normalizePathname(req.url, basePath);\n\t\t\tif (disabledPaths.includes(normalizedPath)) {\n\t\t\t\treturn new Response(\"Not Found\", { status: 404 });\n\t\t\t}\n\n\t\t\tlet currentRequest = req;\n\t\t\tfor (const plugin of ctx.options.plugins || []) {\n\t\t\t\tif (plugin.onRequest) {\n\t\t\t\t\tconst response = await plugin.onRequest(currentRequest, ctx);\n\t\t\t\t\tif (response && \"response\" in response) {\n\t\t\t\t\t\treturn response.response;\n\t\t\t\t\t}\n\t\t\t\t\tif (response && \"request\" in response) {\n\t\t\t\t\t\tcurrentRequest = response.request;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst rateLimitResponse = await onRequestRateLimit(currentRequest, ctx);\n\t\t\tif (rateLimitResponse) {\n\t\t\t\treturn rateLimitResponse;\n\t\t\t}\n\n\t\t\treturn currentRequest;\n\t\t},\n\t\tasync onResponse(res, req) {\n\t\t\tawait onResponseRateLimit(req, ctx);\n\t\t\tfor (const plugin of ctx.options.plugins || []) {\n\t\t\t\tif (plugin.onResponse) {\n\t\t\t\t\tconst response = await plugin.onResponse(res, ctx);\n\t\t\t\t\tif (response) {\n\t\t\t\t\t\treturn response.response;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn res;\n\t\t},\n\t\tonError(e) {\n\t\t\tif (isAPIError(e) && e.status === \"FOUND\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (options.onAPIError?.throw) {\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t\tif (options.onAPIError?.onError) {\n\t\t\t\toptions.onAPIError.onError(e, ctx);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst optLogLevel = options.logger?.level;\n\t\t\tconst log =\n\t\t\t\toptLogLevel === \"error\" ||\n\t\t\t\toptLogLevel === \"warn\" ||\n\t\t\t\toptLogLevel === \"debug\"\n\t\t\t\t\t? logger\n\t\t\t\t\t: undefined;\n\t\t\tif (options.logger?.disabled !== true) {\n\t\t\t\tif (\n\t\t\t\t\te &&\n\t\t\t\t\ttypeof e === \"object\" &&\n\t\t\t\t\t\"message\" in e &&\n\t\t\t\t\ttypeof e.message === \"string\"\n\t\t\t\t) {\n\t\t\t\t\tif (\n\t\t\t\t\t\te.message.includes(\"no column\") ||\n\t\t\t\t\t\te.message.includes(\"column\") ||\n\t\t\t\t\t\te.message.includes(\"relation\") ||\n\t\t\t\t\t\te.message.includes(\"table\") ||\n\t\t\t\t\t\te.message.includes(\"does not exist\")\n\t\t\t\t\t) {\n\t\t\t\t\t\tctx.logger?.error(e.message);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (isAPIError(e)) {\n\t\t\t\t\tif (e.status === \"INTERNAL_SERVER_ERROR\") {\n\t\t\t\t\t\tctx.logger.error(e.status, e);\n\t\t\t\t\t}\n\t\t\t\t\tlog?.error(e.message);\n\t\t\t\t} else {\n\t\t\t\t\tctx.logger?.error(\n\t\t\t\t\t\te && typeof e === \"object\" && \"name\" in e ? (e.name as string) : \"\",\n\t\t\t\t\t\te,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t});\n};\n\nexport {\n\ttype AuthEndpoint,\n\ttype AuthMiddleware,\n\tcreateAuthEndpoint,\n\tcreateAuthMiddleware,\n\toptionsMiddleware,\n} from \"@better-auth/core/api\";\nexport { APIError } from \"@better-auth/core/error\";\nexport { getIp } from \"../utils/get-request-ip\";\nexport { isAPIError } from \"../utils/is-api-error\";\nexport * from \"./middlewares\";\nexport * from \"./routes\";\nexport { getOAuthState } from \"./state/oauth\";\nexport {\n\tgetShouldSkipSessionRefresh,\n\tsetShouldSkipSessionRefresh,\n} from \"./state/should-session-refresh\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/api/index.ts"],"sourcesContent":["import type {\n\tAuthContext,\n\tAwaitable,\n\tBetterAuthOptions,\n\tBetterAuthPlugin,\n} from \"@better-auth/core\";\nimport type { InternalLogger } from \"@better-auth/core/env\";\nimport { logger } from \"@better-auth/core/env\";\nimport {\n\tATTR_CONTEXT,\n\tATTR_HOOK_TYPE,\n\tATTR_HTTP_RESPONSE_STATUS_CODE,\n\tATTR_HTTP_ROUTE,\n\twithSpan,\n} from \"@better-auth/core/instrumentation\";\nimport { normalizePathname } from \"@better-auth/core/utils/url\";\nimport type { Endpoint, Middleware } from \"better-call\";\nimport { createRouter } from \"better-call\";\nimport type { UnionToIntersection } from \"../types\";\nimport { isAPIError } from \"../utils/is-api-error\";\nimport { originCheckMiddleware } from \"./middlewares\";\nimport { onRequestRateLimit, onResponseRateLimit } from \"./rate-limiter\";\nimport {\n\taccountInfo,\n\tcallbackOAuth,\n\tchangeEmail,\n\tchangePassword,\n\tdeleteUser,\n\tdeleteUserCallback,\n\terror,\n\tgetAccessToken,\n\tgetSession,\n\tlinkSocialAccount,\n\tlistSessions,\n\tlistUserAccounts,\n\tok,\n\trefreshToken,\n\trequestPasswordReset,\n\trequestPasswordResetCallback,\n\tresetPassword,\n\trevokeOtherSessions,\n\trevokeSession,\n\trevokeSessions,\n\tsendVerificationEmail,\n\tsetPassword,\n\tsignInEmail,\n\tsignInSocial,\n\tsignOut,\n\tsignUpEmail,\n\tunlinkAccount,\n\tupdateSession,\n\tupdateUser,\n\tverifyEmail,\n\tverifyPassword,\n} from \"./routes\";\nimport { toAuthEndpoints } from \"./to-auth-endpoints\";\n\nexport function checkEndpointConflicts(\n\toptions: BetterAuthOptions,\n\tlogger: InternalLogger,\n) {\n\tconst endpointRegistry = new Map<\n\t\tstring,\n\t\t{ pluginId: string; endpointKey: string; methods: string[] }[]\n\t>();\n\n\toptions.plugins?.forEach((plugin) => {\n\t\tif (plugin.endpoints) {\n\t\t\tfor (const [key, endpoint] of Object.entries(plugin.endpoints)) {\n\t\t\t\tif (\n\t\t\t\t\tendpoint &&\n\t\t\t\t\t\"path\" in endpoint &&\n\t\t\t\t\ttypeof endpoint.path === \"string\"\n\t\t\t\t) {\n\t\t\t\t\tconst path = endpoint.path;\n\t\t\t\t\tlet methods: string[] = [];\n\t\t\t\t\tif (endpoint.options && \"method\" in endpoint.options) {\n\t\t\t\t\t\tif (Array.isArray(endpoint.options.method)) {\n\t\t\t\t\t\t\tmethods = endpoint.options.method;\n\t\t\t\t\t\t} else if (typeof endpoint.options.method === \"string\") {\n\t\t\t\t\t\t\tmethods = [endpoint.options.method];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (methods.length === 0) {\n\t\t\t\t\t\tmethods = [\"*\"];\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!endpointRegistry.has(path)) {\n\t\t\t\t\t\tendpointRegistry.set(path, []);\n\t\t\t\t\t}\n\t\t\t\t\tendpointRegistry.get(path)!.push({\n\t\t\t\t\t\tpluginId: plugin.id,\n\t\t\t\t\t\tendpointKey: key,\n\t\t\t\t\t\tmethods,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\tconst conflicts: {\n\t\tpath: string;\n\t\tplugins: string[];\n\t\tconflictingMethods: string[];\n\t}[] = [];\n\tfor (const [path, entries] of endpointRegistry.entries()) {\n\t\tif (entries.length > 1) {\n\t\t\tconst methodMap = new Map<string, string[]>();\n\t\t\tlet hasConflict = false;\n\n\t\t\tfor (const entry of entries) {\n\t\t\t\tfor (const method of entry.methods) {\n\t\t\t\t\tif (!methodMap.has(method)) {\n\t\t\t\t\t\tmethodMap.set(method, []);\n\t\t\t\t\t}\n\t\t\t\t\tmethodMap.get(method)!.push(entry.pluginId);\n\n\t\t\t\t\tif (methodMap.get(method)!.length > 1) {\n\t\t\t\t\t\thasConflict = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (method === \"*\" && entries.length > 1) {\n\t\t\t\t\t\thasConflict = true;\n\t\t\t\t\t} else if (method !== \"*\" && methodMap.has(\"*\")) {\n\t\t\t\t\t\thasConflict = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (hasConflict) {\n\t\t\t\tconst uniquePlugins = [...new Set(entries.map((e) => e.pluginId))];\n\t\t\t\tconst conflictingMethods: string[] = [];\n\n\t\t\t\tfor (const [method, plugins] of methodMap.entries()) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tplugins.length > 1 ||\n\t\t\t\t\t\t(method === \"*\" && entries.length > 1) ||\n\t\t\t\t\t\t(method !== \"*\" && methodMap.has(\"*\"))\n\t\t\t\t\t) {\n\t\t\t\t\t\tconflictingMethods.push(method);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconflicts.push({\n\t\t\t\t\tpath,\n\t\t\t\t\tplugins: uniquePlugins,\n\t\t\t\t\tconflictingMethods,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tif (conflicts.length > 0) {\n\t\tconst conflictMessages = conflicts\n\t\t\t.map(\n\t\t\t\t(conflict) =>\n\t\t\t\t\t` - \"${conflict.path}\" [${conflict.conflictingMethods.join(\", \")}] used by plugins: ${conflict.plugins.join(\", \")}`,\n\t\t\t)\n\t\t\t.join(\"\\n\");\n\t\tlogger.error(\n\t\t\t`Endpoint path conflicts detected! Multiple plugins are trying to use the same endpoint paths with conflicting HTTP methods:\n${conflictMessages}\n\nTo resolve this, you can:\n\t1. Use only one of the conflicting plugins\n\t2. Configure the plugins to use different paths (if supported)\n\t3. Ensure plugins use different HTTP methods for the same path\n`,\n\t\t);\n\t}\n}\n\nexport function getEndpoints<Option extends BetterAuthOptions>(\n\tctx: Awaitable<AuthContext>,\n\toptions: Option,\n) {\n\tconst pluginEndpoints =\n\t\toptions.plugins?.reduce<Record<string, Endpoint>>((acc, plugin) => {\n\t\t\treturn {\n\t\t\t\t...acc,\n\t\t\t\t...plugin.endpoints,\n\t\t\t};\n\t\t}, {}) ?? {};\n\n\ttype PluginEndpoint = UnionToIntersection<\n\t\tOption[\"plugins\"] extends Array<infer T>\n\t\t\t? T extends BetterAuthPlugin\n\t\t\t\t? T extends {\n\t\t\t\t\t\tendpoints: infer E;\n\t\t\t\t\t}\n\t\t\t\t\t? E\n\t\t\t\t\t: {}\n\t\t\t\t: {}\n\t\t\t: {}\n\t>;\n\n\tconst middlewares =\n\t\toptions.plugins\n\t\t\t?.map((plugin) =>\n\t\t\t\tplugin.middlewares?.map((m) => {\n\t\t\t\t\tconst middleware = (async (context: any) => {\n\t\t\t\t\t\tconst authContext = await ctx;\n\t\t\t\t\t\treturn withSpan(\n\t\t\t\t\t\t\t`middleware ${m.path} ${plugin.id}`,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t[ATTR_HOOK_TYPE]: \"middleware\",\n\t\t\t\t\t\t\t\t[ATTR_HTTP_ROUTE]: m.path,\n\t\t\t\t\t\t\t\t[ATTR_CONTEXT]: `plugin:${plugin.id}`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\t\tm.middleware({\n\t\t\t\t\t\t\t\t\t...context,\n\t\t\t\t\t\t\t\t\tcontext: {\n\t\t\t\t\t\t\t\t\t\t...authContext,\n\t\t\t\t\t\t\t\t\t\t...context.context,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t}) as Middleware;\n\t\t\t\t\tmiddleware.options = m.middleware.options;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpath: m.path,\n\t\t\t\t\t\tmiddleware,\n\t\t\t\t\t};\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.filter((plugin) => plugin !== undefined)\n\t\t\t.flat() || [];\n\n\tconst baseEndpoints = {\n\t\tsignInSocial: signInSocial<Option>(),\n\t\tcallbackOAuth,\n\t\tgetSession: getSession<Option>(),\n\t\tsignOut,\n\t\tsignUpEmail: signUpEmail<Option>(),\n\t\tsignInEmail: signInEmail<Option>(),\n\t\tresetPassword,\n\t\tverifyPassword,\n\t\tverifyEmail,\n\t\tsendVerificationEmail,\n\t\tchangeEmail,\n\t\tchangePassword,\n\t\tsetPassword,\n\t\tupdateSession: updateSession<Option>(),\n\t\tupdateUser: updateUser<Option>(),\n\t\tdeleteUser,\n\t\trequestPasswordReset,\n\t\trequestPasswordResetCallback,\n\t\tlistSessions: listSessions<Option>(),\n\t\trevokeSession,\n\t\trevokeSessions,\n\t\trevokeOtherSessions,\n\t\tlinkSocialAccount,\n\t\tlistUserAccounts,\n\t\tdeleteUserCallback,\n\t\tunlinkAccount,\n\t\trefreshToken,\n\t\tgetAccessToken,\n\t\taccountInfo,\n\t};\n\tconst endpoints = {\n\t\t...baseEndpoints,\n\t\t...pluginEndpoints,\n\t\tok,\n\t\terror,\n\t} as const;\n\tconst api = toAuthEndpoints(endpoints, ctx);\n\treturn {\n\t\tapi: api as typeof endpoints & PluginEndpoint,\n\t\tmiddlewares,\n\t};\n}\nexport const router = <Option extends BetterAuthOptions>(\n\tctx: AuthContext,\n\toptions: Option,\n) => {\n\tconst { api, middlewares } = getEndpoints(ctx, options);\n\tconst basePath = new URL(ctx.baseURL).pathname;\n\n\treturn createRouter(api, {\n\t\trouterContext: ctx,\n\t\topenapi: {\n\t\t\tdisabled: true,\n\t\t},\n\t\tbasePath,\n\t\trouterMiddleware: [\n\t\t\t{\n\t\t\t\tpath: \"/**\",\n\t\t\t\tmiddleware: originCheckMiddleware,\n\t\t\t},\n\t\t\t...middlewares,\n\t\t],\n\t\tallowedMediaTypes: [\"application/json\"],\n\t\tskipTrailingSlashes: options.advanced?.skipTrailingSlashes ?? false,\n\t\tasync onRequest(req) {\n\t\t\t//handle disabled paths\n\t\t\tconst disabledPaths = ctx.options.disabledPaths || [];\n\t\t\tconst normalizedPath = normalizePathname(req.url, basePath);\n\t\t\tif (disabledPaths.includes(normalizedPath)) {\n\t\t\t\treturn new Response(\"Not Found\", { status: 404 });\n\t\t\t}\n\n\t\t\tlet currentRequest = req;\n\t\t\tfor (const plugin of ctx.options.plugins || []) {\n\t\t\t\tif (plugin.onRequest) {\n\t\t\t\t\tconst response = await withSpan(\n\t\t\t\t\t\t`onRequest ${normalizedPath} ${plugin.id}`,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t[ATTR_HOOK_TYPE]: \"onRequest\",\n\t\t\t\t\t\t\t[ATTR_CONTEXT]: `plugin:${plugin.id}`,\n\t\t\t\t\t\t\t[ATTR_HTTP_ROUTE]: normalizedPath,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t() => plugin.onRequest!(currentRequest, ctx),\n\t\t\t\t\t);\n\t\t\t\t\tif (response && \"response\" in response) {\n\t\t\t\t\t\treturn response.response;\n\t\t\t\t\t}\n\t\t\t\t\tif (response && \"request\" in response) {\n\t\t\t\t\t\tcurrentRequest = response.request;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst rateLimitResponse = await onRequestRateLimit(currentRequest, ctx);\n\t\t\tif (rateLimitResponse) {\n\t\t\t\treturn rateLimitResponse;\n\t\t\t}\n\n\t\t\treturn currentRequest;\n\t\t},\n\t\tasync onResponse(res, req) {\n\t\t\tawait onResponseRateLimit(req, ctx);\n\t\t\tconst normalizedPath = normalizePathname(req.url, basePath);\n\t\t\tfor (const plugin of ctx.options.plugins || []) {\n\t\t\t\tif (plugin.onResponse) {\n\t\t\t\t\tconst response = await withSpan(\n\t\t\t\t\t\t`onResponse ${normalizedPath} ${plugin.id}`,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t[ATTR_HOOK_TYPE]: \"onResponse\",\n\t\t\t\t\t\t\t[ATTR_CONTEXT]: `plugin:${plugin.id}`,\n\t\t\t\t\t\t\t[ATTR_HTTP_ROUTE]: normalizedPath,\n\t\t\t\t\t\t\t[ATTR_HTTP_RESPONSE_STATUS_CODE]: res.status,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t() => plugin.onResponse!(res, ctx),\n\t\t\t\t\t);\n\t\t\t\t\tif (response) {\n\t\t\t\t\t\treturn response.response;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn res;\n\t\t},\n\t\tonError(e) {\n\t\t\tif (isAPIError(e) && e.status === \"FOUND\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (options.onAPIError?.throw) {\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t\tif (options.onAPIError?.onError) {\n\t\t\t\toptions.onAPIError.onError(e, ctx);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst optLogLevel = options.logger?.level;\n\t\t\tconst log =\n\t\t\t\toptLogLevel === \"error\" ||\n\t\t\t\toptLogLevel === \"warn\" ||\n\t\t\t\toptLogLevel === \"debug\"\n\t\t\t\t\t? logger\n\t\t\t\t\t: undefined;\n\t\t\tif (options.logger?.disabled !== true) {\n\t\t\t\tif (\n\t\t\t\t\te &&\n\t\t\t\t\ttypeof e === \"object\" &&\n\t\t\t\t\t\"message\" in e &&\n\t\t\t\t\ttypeof e.message === \"string\"\n\t\t\t\t) {\n\t\t\t\t\tif (\n\t\t\t\t\t\te.message.includes(\"no column\") ||\n\t\t\t\t\t\te.message.includes(\"column\") ||\n\t\t\t\t\t\te.message.includes(\"relation\") ||\n\t\t\t\t\t\te.message.includes(\"table\") ||\n\t\t\t\t\t\te.message.includes(\"does not exist\")\n\t\t\t\t\t) {\n\t\t\t\t\t\tctx.logger?.error(e.message);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (isAPIError(e)) {\n\t\t\t\t\tif (e.status === \"INTERNAL_SERVER_ERROR\") {\n\t\t\t\t\t\tctx.logger.error(e.status, e);\n\t\t\t\t\t}\n\t\t\t\t\tlog?.error(e.message);\n\t\t\t\t} else {\n\t\t\t\t\tctx.logger?.error(\n\t\t\t\t\t\te && typeof e === \"object\" && \"name\" in e ? (e.name as string) : \"\",\n\t\t\t\t\t\te,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t});\n};\n\nexport {\n\ttype AuthEndpoint,\n\ttype AuthMiddleware,\n\tcreateAuthEndpoint,\n\tcreateAuthMiddleware,\n\toptionsMiddleware,\n} from \"@better-auth/core/api\";\nexport { APIError } from \"@better-auth/core/error\";\nexport { getIp } from \"../utils/get-request-ip\";\nexport { isAPIError } from \"../utils/is-api-error\";\nexport * from \"./middlewares\";\nexport * from \"./routes\";\nexport { getOAuthState } from \"./state/oauth\";\nexport {\n\tgetShouldSkipSessionRefresh,\n\tsetShouldSkipSessionRefresh,\n} from \"./state/should-session-refresh\";\n"],"mappings":"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"}
@@ -15,6 +15,20 @@ function shouldSkipCSRFForBackwardCompat(ctx) {
15
15
  return ctx.context.skipOriginCheck === true && ctx.context.options.advanced?.disableCSRFCheck === void 0;
16
16
  }
17
17
  /**
18
+ * Checks if the origin check should be skipped for the current request.
19
+ * Handles both boolean (skip all) and array (skip specific paths) configurations.
20
+ */
21
+ function shouldSkipOriginCheck(ctx) {
22
+ const skipOriginCheck = ctx.context.skipOriginCheck;
23
+ if (skipOriginCheck === true) return true;
24
+ if (Array.isArray(skipOriginCheck) && ctx.request) try {
25
+ const basePath = new URL(ctx.context.baseURL).pathname;
26
+ const currentPath = normalizePathname(ctx.request.url, basePath);
27
+ return skipOriginCheck.some((skipPath) => currentPath.startsWith(skipPath));
28
+ } catch {}
29
+ return false;
30
+ }
31
+ /**
18
32
  * Logs deprecation warning for users relying on coupled behavior.
19
33
  * Only logs if user explicitly set disableOriginCheck (not test environment default).
20
34
  */
@@ -26,7 +40,7 @@ const logBackwardCompatWarning = deprecate(function logBackwardCompatWarning() {
26
40
  const originCheckMiddleware = createAuthMiddleware(async (ctx) => {
27
41
  if (ctx.request?.method === "GET" || ctx.request?.method === "OPTIONS" || ctx.request?.method === "HEAD" || !ctx.request) return;
28
42
  await validateOrigin(ctx);
29
- if (ctx.context.skipOriginCheck) return;
43
+ if (shouldSkipOriginCheck(ctx)) return;
30
44
  const { body, query } = ctx;
31
45
  const callbackURL = body?.callbackURL || query?.callbackURL;
32
46
  const redirectURL = body?.redirectTo;
@@ -52,7 +66,7 @@ const originCheckMiddleware = createAuthMiddleware(async (ctx) => {
52
66
  });
53
67
  const originCheck = (getValue) => createAuthMiddleware(async (ctx) => {
54
68
  if (!ctx.request) return;
55
- if (ctx.context.skipOriginCheck) return;
69
+ if (shouldSkipOriginCheck(ctx)) return;
56
70
  const callbackURL = getValue(ctx);
57
71
  const validateURL = (url, label) => {
58
72
  if (!url) return;
@@ -85,12 +99,7 @@ async function validateOrigin(ctx, forceValidate = false) {
85
99
  ctx.context.options.advanced?.disableOriginCheck === true && logBackwardCompatWarning();
86
100
  return;
87
101
  }
88
- const skipOriginCheck = ctx.context.skipOriginCheck;
89
- if (Array.isArray(skipOriginCheck)) try {
90
- const basePath = new URL(ctx.context.baseURL).pathname;
91
- const currentPath = normalizePathname(ctx.request.url, basePath);
92
- if (skipOriginCheck.some((skipPath) => currentPath.startsWith(skipPath))) return;
93
- } catch {}
102
+ if (shouldSkipOriginCheck(ctx)) return;
94
103
  if (!(forceValidate || useCookies)) return;
95
104
  if (!originHeader || originHeader === "null") throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.MISSING_OR_NULL_ORIGIN);
96
105
  const trustedOrigins = Array.isArray(ctx.context.options.trustedOrigins) ? ctx.context.trustedOrigins : [...ctx.context.trustedOrigins, ...(await ctx.context.options.trustedOrigins?.(ctx.request))?.filter((v) => Boolean(v)) || []];
@@ -1 +1 @@
1
- {"version":3,"file":"origin-check.mjs","names":[],"sources":["../../../src/api/middlewares/origin-check.ts"],"sourcesContent":["import type { GenericEndpointContext } from \"@better-auth/core\";\nimport { createAuthMiddleware } from \"@better-auth/core/api\";\nimport { APIError, BASE_ERROR_CODES } from \"@better-auth/core/error\";\nimport { deprecate } from \"@better-auth/core/utils/deprecate\";\nimport { normalizePathname } from \"@better-auth/core/utils/url\";\nimport { matchesOriginPattern } from \"../../auth/trusted-origins\";\n\n/**\n * Checks if CSRF should be skipped for backward compatibility.\n * Previously, disableOriginCheck also disabled CSRF checks.\n * This maintains that behavior when disableCSRFCheck isn't explicitly set.\n * Only triggers for skipOriginCheck === true, not for path arrays.\n */\nfunction shouldSkipCSRFForBackwardCompat(ctx: GenericEndpointContext): boolean {\n\treturn (\n\t\tctx.context.skipOriginCheck === true &&\n\t\tctx.context.options.advanced?.disableCSRFCheck === undefined\n\t);\n}\n\n/**\n * Logs deprecation warning for users relying on coupled behavior.\n * Only logs if user explicitly set disableOriginCheck (not test environment default).\n */\nconst logBackwardCompatWarning = deprecate(\n\tfunction logBackwardCompatWarning() {},\n\t\"disableOriginCheck: true currently also disables CSRF checks. \" +\n\t\t\"In a future version, disableOriginCheck will ONLY disable URL validation. \" +\n\t\t\"To keep CSRF disabled, add disableCSRFCheck: true to your config.\",\n);\n\n/**\n * A middleware to validate callbackURL and origin against trustedOrigins.\n * Also handles CSRF protection using Fetch Metadata for first-login scenarios.\n */\nexport const originCheckMiddleware = createAuthMiddleware(async (ctx) => {\n\t// Skip origin check for GET, OPTIONS, HEAD requests - we don't mutate state here.\n\tif (\n\t\tctx.request?.method === \"GET\" ||\n\t\tctx.request?.method === \"OPTIONS\" ||\n\t\tctx.request?.method === \"HEAD\" ||\n\t\t!ctx.request\n\t) {\n\t\treturn;\n\t}\n\tawait validateOrigin(ctx);\n\n\tif (ctx.context.skipOriginCheck) {\n\t\treturn;\n\t}\n\n\tconst { body, query } = ctx;\n\tconst callbackURL = body?.callbackURL || query?.callbackURL;\n\tconst redirectURL = body?.redirectTo;\n\tconst errorCallbackURL = body?.errorCallbackURL;\n\tconst newUserCallbackURL = body?.newUserCallbackURL;\n\n\tconst validateURL = (\n\t\turl: string | undefined,\n\t\tlabel:\n\t\t\t| \"origin\"\n\t\t\t| \"callbackURL\"\n\t\t\t| \"redirectURL\"\n\t\t\t| \"errorCallbackURL\"\n\t\t\t| \"newUserCallbackURL\",\n\t) => {\n\t\tif (!url) {\n\t\t\treturn;\n\t\t}\n\t\tconst isTrustedOrigin = ctx.context.isTrustedOrigin(url, {\n\t\t\tallowRelativePaths: label !== \"origin\",\n\t\t});\n\n\t\tif (!isTrustedOrigin) {\n\t\t\tctx.context.logger.error(`Invalid ${label}: ${url}`);\n\t\t\tctx.context.logger.info(\n\t\t\t\t`If it's a valid URL, please add ${url} to trustedOrigins in your auth config\\n`,\n\t\t\t\t`Current list of trustedOrigins: ${ctx.context.trustedOrigins}`,\n\t\t\t);\n\t\t\tif (label === \"origin\") {\n\t\t\t\tthrow APIError.from(\"FORBIDDEN\", BASE_ERROR_CODES.INVALID_ORIGIN);\n\t\t\t}\n\t\t\tif (label === \"callbackURL\") {\n\t\t\t\tthrow APIError.from(\"FORBIDDEN\", BASE_ERROR_CODES.INVALID_CALLBACK_URL);\n\t\t\t}\n\t\t\tif (label === \"redirectURL\") {\n\t\t\t\tthrow APIError.from(\"FORBIDDEN\", BASE_ERROR_CODES.INVALID_REDIRECT_URL);\n\t\t\t}\n\t\t\tif (label === \"errorCallbackURL\") {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tBASE_ERROR_CODES.INVALID_ERROR_CALLBACK_URL,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (label === \"newUserCallbackURL\") {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tBASE_ERROR_CODES.INVALID_NEW_USER_CALLBACK_URL,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow APIError.fromStatus(\"FORBIDDEN\", {\n\t\t\t\tmessage: `Invalid ${label}`,\n\t\t\t});\n\t\t}\n\t};\n\n\tcallbackURL && validateURL(callbackURL, \"callbackURL\");\n\tredirectURL && validateURL(redirectURL, \"redirectURL\");\n\terrorCallbackURL && validateURL(errorCallbackURL, \"errorCallbackURL\");\n\tnewUserCallbackURL && validateURL(newUserCallbackURL, \"newUserCallbackURL\");\n});\n\nexport const originCheck = (\n\tgetValue: (ctx: GenericEndpointContext) => string | string[],\n) =>\n\tcreateAuthMiddleware(async (ctx) => {\n\t\tif (!ctx.request) {\n\t\t\treturn;\n\t\t}\n\t\tif (ctx.context.skipOriginCheck) {\n\t\t\treturn;\n\t\t}\n\t\tconst callbackURL = getValue(ctx);\n\t\tconst validateURL = (url: string | undefined, label: string) => {\n\t\t\tif (!url) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst isTrustedOrigin = ctx.context.isTrustedOrigin(url, {\n\t\t\t\tallowRelativePaths: label !== \"origin\",\n\t\t\t});\n\n\t\t\tif (!isTrustedOrigin) {\n\t\t\t\tctx.context.logger.error(`Invalid ${label}: ${url}`);\n\t\t\t\tctx.context.logger.info(\n\t\t\t\t\t`If it's a valid URL, please add ${url} to trustedOrigins in your auth config\\n`,\n\t\t\t\t\t`Current list of trustedOrigins: ${ctx.context.trustedOrigins}`,\n\t\t\t\t);\n\t\t\t\tif (label === \"origin\") {\n\t\t\t\t\tthrow APIError.from(\"FORBIDDEN\", BASE_ERROR_CODES.INVALID_ORIGIN);\n\t\t\t\t}\n\t\t\t\tif (label === \"callbackURL\") {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tBASE_ERROR_CODES.INVALID_CALLBACK_URL,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (label === \"redirectURL\") {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tBASE_ERROR_CODES.INVALID_REDIRECT_URL,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (label === \"errorCallbackURL\") {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tBASE_ERROR_CODES.INVALID_ERROR_CALLBACK_URL,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (label === \"newUserCallbackURL\") {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tBASE_ERROR_CODES.INVALID_NEW_USER_CALLBACK_URL,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow APIError.fromStatus(\"FORBIDDEN\", {\n\t\t\t\t\tmessage: `Invalid ${label}`,\n\t\t\t\t});\n\t\t\t}\n\t\t};\n\t\tconst callbacks = Array.isArray(callbackURL) ? callbackURL : [callbackURL];\n\t\tfor (const url of callbacks) {\n\t\t\tvalidateURL(url, \"callbackURL\");\n\t\t}\n\t});\n\n/**\n * Validates origin header against trusted origins.\n * @param ctx - The endpoint context\n * @param forceValidate - If true, always validate origin regardless of cookies/skip flags\n */\nasync function validateOrigin(\n\tctx: GenericEndpointContext,\n\tforceValidate = false,\n): Promise<void> {\n\tconst headers = ctx.request?.headers;\n\tif (!headers || !ctx.request) {\n\t\treturn;\n\t}\n\tconst originHeader = headers.get(\"origin\") || headers.get(\"referer\") || \"\";\n\tconst useCookies = headers.has(\"cookie\");\n\n\tif (ctx.context.skipCSRFCheck) {\n\t\treturn;\n\t}\n\n\tif (shouldSkipCSRFForBackwardCompat(ctx)) {\n\t\tctx.context.options.advanced?.disableOriginCheck === true &&\n\t\t\tlogBackwardCompatWarning();\n\t\treturn;\n\t}\n\n\tconst skipOriginCheck = ctx.context.skipOriginCheck;\n\tif (Array.isArray(skipOriginCheck)) {\n\t\ttry {\n\t\t\tconst basePath = new URL(ctx.context.baseURL).pathname;\n\t\t\tconst currentPath = normalizePathname(ctx.request.url, basePath);\n\t\t\tconst shouldSkipPath = skipOriginCheck.some((skipPath) =>\n\t\t\t\tcurrentPath.startsWith(skipPath),\n\t\t\t);\n\t\t\tif (shouldSkipPath) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch {\n\t\t\t// If parsing fails, don't skip - continue with validation\n\t\t}\n\t}\n\n\tconst shouldValidate = forceValidate || useCookies;\n\n\tif (!shouldValidate) {\n\t\treturn;\n\t}\n\n\tif (!originHeader || originHeader === \"null\") {\n\t\tthrow APIError.from(\"FORBIDDEN\", BASE_ERROR_CODES.MISSING_OR_NULL_ORIGIN);\n\t}\n\n\tconst trustedOrigins: string[] = Array.isArray(\n\t\tctx.context.options.trustedOrigins,\n\t)\n\t\t? ctx.context.trustedOrigins\n\t\t: [\n\t\t\t\t...ctx.context.trustedOrigins,\n\t\t\t\t...((await ctx.context.options.trustedOrigins?.(ctx.request))?.filter(\n\t\t\t\t\t(v): v is string => Boolean(v),\n\t\t\t\t) || []),\n\t\t\t];\n\n\tconst isTrustedOrigin = trustedOrigins.some((origin) =>\n\t\tmatchesOriginPattern(originHeader, origin),\n\t);\n\tif (!isTrustedOrigin) {\n\t\tctx.context.logger.error(`Invalid origin: ${originHeader}`);\n\t\tctx.context.logger.info(\n\t\t\t`If it's a valid URL, please add ${originHeader} to trustedOrigins in your auth config\\n`,\n\t\t\t`Current list of trustedOrigins: ${trustedOrigins}`,\n\t\t);\n\t\tthrow APIError.from(\"FORBIDDEN\", BASE_ERROR_CODES.INVALID_ORIGIN);\n\t}\n}\n\n/**\n * Middleware for CSRF protection using Fetch Metadata headers.\n * This prevents cross-site navigation login attacks while supporting progressive enhancement.\n */\nexport const formCsrfMiddleware = createAuthMiddleware(async (ctx) => {\n\tconst request = ctx.request;\n\tif (!request) {\n\t\treturn;\n\t}\n\n\tawait validateFormCsrf(ctx);\n});\n\n/**\n * Validates CSRF protection for first-login scenarios using Fetch Metadata headers.\n * This prevents cross-site form submission attacks while supporting progressive enhancement.\n */\nasync function validateFormCsrf(ctx: GenericEndpointContext): Promise<void> {\n\tconst req = ctx.request;\n\tif (!req) {\n\t\treturn;\n\t}\n\n\tif (ctx.context.skipCSRFCheck) {\n\t\treturn;\n\t}\n\n\tif (shouldSkipCSRFForBackwardCompat(ctx)) {\n\t\treturn;\n\t}\n\n\tconst headers = req.headers;\n\tconst hasAnyCookies = headers.has(\"cookie\");\n\n\tif (hasAnyCookies) {\n\t\treturn await validateOrigin(ctx);\n\t}\n\n\tconst site = headers.get(\"Sec-Fetch-Site\");\n\tconst mode = headers.get(\"Sec-Fetch-Mode\");\n\tconst dest = headers.get(\"Sec-Fetch-Dest\");\n\n\tconst hasMetadata = Boolean(\n\t\t(site && site.trim()) || (mode && mode.trim()) || (dest && dest.trim()),\n\t);\n\n\tif (hasMetadata) {\n\t\t// Block cross-site navigation requests (classic CSRF attack pattern)\n\t\tif (site === \"cross-site\" && mode === \"navigate\") {\n\t\t\tctx.context.logger.error(\n\t\t\t\t\"Blocked cross-site navigation login attempt (CSRF protection)\",\n\t\t\t\t{\n\t\t\t\t\tsecFetchSite: site,\n\t\t\t\t\tsecFetchMode: mode,\n\t\t\t\t\tsecFetchDest: dest,\n\t\t\t\t},\n\t\t\t);\n\t\t\tthrow APIError.from(\n\t\t\t\t\"FORBIDDEN\",\n\t\t\t\tBASE_ERROR_CODES.CROSS_SITE_NAVIGATION_LOGIN_BLOCKED,\n\t\t\t);\n\t\t}\n\n\t\treturn await validateOrigin(ctx, true);\n\t}\n\n\t// No cookies, no Fetch Metadata → fallback to old behavior (no validation)\n\treturn;\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAS,gCAAgC,KAAsC;AAC9E,QACC,IAAI,QAAQ,oBAAoB,QAChC,IAAI,QAAQ,QAAQ,UAAU,qBAAqB;;;;;;AAQrD,MAAM,2BAA2B,UAChC,SAAS,2BAA2B,IACpC,4MAGA;;;;;AAMD,MAAa,wBAAwB,qBAAqB,OAAO,QAAQ;AAExE,KACC,IAAI,SAAS,WAAW,SACxB,IAAI,SAAS,WAAW,aACxB,IAAI,SAAS,WAAW,UACxB,CAAC,IAAI,QAEL;AAED,OAAM,eAAe,IAAI;AAEzB,KAAI,IAAI,QAAQ,gBACf;CAGD,MAAM,EAAE,MAAM,UAAU;CACxB,MAAM,cAAc,MAAM,eAAe,OAAO;CAChD,MAAM,cAAc,MAAM;CAC1B,MAAM,mBAAmB,MAAM;CAC/B,MAAM,qBAAqB,MAAM;CAEjC,MAAM,eACL,KACA,UAMI;AACJ,MAAI,CAAC,IACJ;AAMD,MAAI,CAJoB,IAAI,QAAQ,gBAAgB,KAAK,EACxD,oBAAoB,UAAU,UAC9B,CAAC,EAEoB;AACrB,OAAI,QAAQ,OAAO,MAAM,WAAW,MAAM,IAAI,MAAM;AACpD,OAAI,QAAQ,OAAO,KAClB,mCAAmC,IAAI,2CACvC,mCAAmC,IAAI,QAAQ,iBAC/C;AACD,OAAI,UAAU,SACb,OAAM,SAAS,KAAK,aAAa,iBAAiB,eAAe;AAElE,OAAI,UAAU,cACb,OAAM,SAAS,KAAK,aAAa,iBAAiB,qBAAqB;AAExE,OAAI,UAAU,cACb,OAAM,SAAS,KAAK,aAAa,iBAAiB,qBAAqB;AAExE,OAAI,UAAU,mBACb,OAAM,SAAS,KACd,aACA,iBAAiB,2BACjB;AAEF,OAAI,UAAU,qBACb,OAAM,SAAS,KACd,aACA,iBAAiB,8BACjB;AAEF,SAAM,SAAS,WAAW,aAAa,EACtC,SAAS,WAAW,SACpB,CAAC;;;AAIJ,gBAAe,YAAY,aAAa,cAAc;AACtD,gBAAe,YAAY,aAAa,cAAc;AACtD,qBAAoB,YAAY,kBAAkB,mBAAmB;AACrE,uBAAsB,YAAY,oBAAoB,qBAAqB;EAC1E;AAEF,MAAa,eACZ,aAEA,qBAAqB,OAAO,QAAQ;AACnC,KAAI,CAAC,IAAI,QACR;AAED,KAAI,IAAI,QAAQ,gBACf;CAED,MAAM,cAAc,SAAS,IAAI;CACjC,MAAM,eAAe,KAAyB,UAAkB;AAC/D,MAAI,CAAC,IACJ;AAMD,MAAI,CAJoB,IAAI,QAAQ,gBAAgB,KAAK,EACxD,oBAAoB,UAAU,UAC9B,CAAC,EAEoB;AACrB,OAAI,QAAQ,OAAO,MAAM,WAAW,MAAM,IAAI,MAAM;AACpD,OAAI,QAAQ,OAAO,KAClB,mCAAmC,IAAI,2CACvC,mCAAmC,IAAI,QAAQ,iBAC/C;AACD,OAAI,UAAU,SACb,OAAM,SAAS,KAAK,aAAa,iBAAiB,eAAe;AAElE,OAAI,UAAU,cACb,OAAM,SAAS,KACd,aACA,iBAAiB,qBACjB;AAEF,OAAI,UAAU,cACb,OAAM,SAAS,KACd,aACA,iBAAiB,qBACjB;AAEF,OAAI,UAAU,mBACb,OAAM,SAAS,KACd,aACA,iBAAiB,2BACjB;AAEF,OAAI,UAAU,qBACb,OAAM,SAAS,KACd,aACA,iBAAiB,8BACjB;AAEF,SAAM,SAAS,WAAW,aAAa,EACtC,SAAS,WAAW,SACpB,CAAC;;;CAGJ,MAAM,YAAY,MAAM,QAAQ,YAAY,GAAG,cAAc,CAAC,YAAY;AAC1E,MAAK,MAAM,OAAO,UACjB,aAAY,KAAK,cAAc;EAE/B;;;;;;AAOH,eAAe,eACd,KACA,gBAAgB,OACA;CAChB,MAAM,UAAU,IAAI,SAAS;AAC7B,KAAI,CAAC,WAAW,CAAC,IAAI,QACpB;CAED,MAAM,eAAe,QAAQ,IAAI,SAAS,IAAI,QAAQ,IAAI,UAAU,IAAI;CACxE,MAAM,aAAa,QAAQ,IAAI,SAAS;AAExC,KAAI,IAAI,QAAQ,cACf;AAGD,KAAI,gCAAgC,IAAI,EAAE;AACzC,MAAI,QAAQ,QAAQ,UAAU,uBAAuB,QACpD,0BAA0B;AAC3B;;CAGD,MAAM,kBAAkB,IAAI,QAAQ;AACpC,KAAI,MAAM,QAAQ,gBAAgB,CACjC,KAAI;EACH,MAAM,WAAW,IAAI,IAAI,IAAI,QAAQ,QAAQ,CAAC;EAC9C,MAAM,cAAc,kBAAkB,IAAI,QAAQ,KAAK,SAAS;AAIhE,MAHuB,gBAAgB,MAAM,aAC5C,YAAY,WAAW,SAAS,CAChC,CAEA;SAEM;AAOT,KAAI,EAFmB,iBAAiB,YAGvC;AAGD,KAAI,CAAC,gBAAgB,iBAAiB,OACrC,OAAM,SAAS,KAAK,aAAa,iBAAiB,uBAAuB;CAG1E,MAAM,iBAA2B,MAAM,QACtC,IAAI,QAAQ,QAAQ,eACpB,GACE,IAAI,QAAQ,iBACZ,CACA,GAAG,IAAI,QAAQ,gBACf,IAAK,MAAM,IAAI,QAAQ,QAAQ,iBAAiB,IAAI,QAAQ,GAAG,QAC7D,MAAmB,QAAQ,EAAE,CAC9B,IAAI,EAAE,CACP;AAKH,KAAI,CAHoB,eAAe,MAAM,WAC5C,qBAAqB,cAAc,OAAO,CAC1C,EACqB;AACrB,MAAI,QAAQ,OAAO,MAAM,mBAAmB,eAAe;AAC3D,MAAI,QAAQ,OAAO,KAClB,mCAAmC,aAAa,2CAChD,mCAAmC,iBACnC;AACD,QAAM,SAAS,KAAK,aAAa,iBAAiB,eAAe;;;;;;;AAQnE,MAAa,qBAAqB,qBAAqB,OAAO,QAAQ;AAErE,KAAI,CADY,IAAI,QAEnB;AAGD,OAAM,iBAAiB,IAAI;EAC1B;;;;;AAMF,eAAe,iBAAiB,KAA4C;CAC3E,MAAM,MAAM,IAAI;AAChB,KAAI,CAAC,IACJ;AAGD,KAAI,IAAI,QAAQ,cACf;AAGD,KAAI,gCAAgC,IAAI,CACvC;CAGD,MAAM,UAAU,IAAI;AAGpB,KAFsB,QAAQ,IAAI,SAAS,CAG1C,QAAO,MAAM,eAAe,IAAI;CAGjC,MAAM,OAAO,QAAQ,IAAI,iBAAiB;CAC1C,MAAM,OAAO,QAAQ,IAAI,iBAAiB;CAC1C,MAAM,OAAO,QAAQ,IAAI,iBAAiB;AAM1C,KAJoB,QAClB,QAAQ,KAAK,MAAM,IAAM,QAAQ,KAAK,MAAM,IAAM,QAAQ,KAAK,MAAM,CACtE,EAEgB;AAEhB,MAAI,SAAS,gBAAgB,SAAS,YAAY;AACjD,OAAI,QAAQ,OAAO,MAClB,iEACA;IACC,cAAc;IACd,cAAc;IACd,cAAc;IACd,CACD;AACD,SAAM,SAAS,KACd,aACA,iBAAiB,oCACjB;;AAGF,SAAO,MAAM,eAAe,KAAK,KAAK"}
1
+ {"version":3,"file":"origin-check.mjs","names":[],"sources":["../../../src/api/middlewares/origin-check.ts"],"sourcesContent":["import type { GenericEndpointContext } from \"@better-auth/core\";\nimport { createAuthMiddleware } from \"@better-auth/core/api\";\nimport { APIError, BASE_ERROR_CODES } from \"@better-auth/core/error\";\nimport { deprecate } from \"@better-auth/core/utils/deprecate\";\nimport { normalizePathname } from \"@better-auth/core/utils/url\";\nimport { matchesOriginPattern } from \"../../auth/trusted-origins\";\n\n/**\n * Checks if CSRF should be skipped for backward compatibility.\n * Previously, disableOriginCheck also disabled CSRF checks.\n * This maintains that behavior when disableCSRFCheck isn't explicitly set.\n * Only triggers for skipOriginCheck === true, not for path arrays.\n */\nfunction shouldSkipCSRFForBackwardCompat(ctx: GenericEndpointContext): boolean {\n\treturn (\n\t\tctx.context.skipOriginCheck === true &&\n\t\tctx.context.options.advanced?.disableCSRFCheck === undefined\n\t);\n}\n\n/**\n * Checks if the origin check should be skipped for the current request.\n * Handles both boolean (skip all) and array (skip specific paths) configurations.\n */\nfunction shouldSkipOriginCheck(ctx: GenericEndpointContext): boolean {\n\tconst skipOriginCheck = ctx.context.skipOriginCheck;\n\tif (skipOriginCheck === true) {\n\t\treturn true;\n\t}\n\tif (Array.isArray(skipOriginCheck) && ctx.request) {\n\t\ttry {\n\t\t\tconst basePath = new URL(ctx.context.baseURL).pathname;\n\t\t\tconst currentPath = normalizePathname(ctx.request.url, basePath);\n\t\t\treturn skipOriginCheck.some((skipPath) =>\n\t\t\t\tcurrentPath.startsWith(skipPath),\n\t\t\t);\n\t\t} catch {\n\t\t\t//\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * Logs deprecation warning for users relying on coupled behavior.\n * Only logs if user explicitly set disableOriginCheck (not test environment default).\n */\nconst logBackwardCompatWarning = deprecate(\n\tfunction logBackwardCompatWarning() {},\n\t\"disableOriginCheck: true currently also disables CSRF checks. \" +\n\t\t\"In a future version, disableOriginCheck will ONLY disable URL validation. \" +\n\t\t\"To keep CSRF disabled, add disableCSRFCheck: true to your config.\",\n);\n\n/**\n * A middleware to validate callbackURL and origin against trustedOrigins.\n * Also handles CSRF protection using Fetch Metadata for first-login scenarios.\n */\nexport const originCheckMiddleware = createAuthMiddleware(async (ctx) => {\n\t// Skip origin check for GET, OPTIONS, HEAD requests - we don't mutate state here.\n\tif (\n\t\tctx.request?.method === \"GET\" ||\n\t\tctx.request?.method === \"OPTIONS\" ||\n\t\tctx.request?.method === \"HEAD\" ||\n\t\t!ctx.request\n\t) {\n\t\treturn;\n\t}\n\tawait validateOrigin(ctx);\n\n\tif (shouldSkipOriginCheck(ctx)) {\n\t\treturn;\n\t}\n\n\tconst { body, query } = ctx;\n\tconst callbackURL = body?.callbackURL || query?.callbackURL;\n\tconst redirectURL = body?.redirectTo;\n\tconst errorCallbackURL = body?.errorCallbackURL;\n\tconst newUserCallbackURL = body?.newUserCallbackURL;\n\n\tconst validateURL = (\n\t\turl: string | undefined,\n\t\tlabel:\n\t\t\t| \"origin\"\n\t\t\t| \"callbackURL\"\n\t\t\t| \"redirectURL\"\n\t\t\t| \"errorCallbackURL\"\n\t\t\t| \"newUserCallbackURL\",\n\t) => {\n\t\tif (!url) {\n\t\t\treturn;\n\t\t}\n\t\tconst isTrustedOrigin = ctx.context.isTrustedOrigin(url, {\n\t\t\tallowRelativePaths: label !== \"origin\",\n\t\t});\n\n\t\tif (!isTrustedOrigin) {\n\t\t\tctx.context.logger.error(`Invalid ${label}: ${url}`);\n\t\t\tctx.context.logger.info(\n\t\t\t\t`If it's a valid URL, please add ${url} to trustedOrigins in your auth config\\n`,\n\t\t\t\t`Current list of trustedOrigins: ${ctx.context.trustedOrigins}`,\n\t\t\t);\n\t\t\tif (label === \"origin\") {\n\t\t\t\tthrow APIError.from(\"FORBIDDEN\", BASE_ERROR_CODES.INVALID_ORIGIN);\n\t\t\t}\n\t\t\tif (label === \"callbackURL\") {\n\t\t\t\tthrow APIError.from(\"FORBIDDEN\", BASE_ERROR_CODES.INVALID_CALLBACK_URL);\n\t\t\t}\n\t\t\tif (label === \"redirectURL\") {\n\t\t\t\tthrow APIError.from(\"FORBIDDEN\", BASE_ERROR_CODES.INVALID_REDIRECT_URL);\n\t\t\t}\n\t\t\tif (label === \"errorCallbackURL\") {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tBASE_ERROR_CODES.INVALID_ERROR_CALLBACK_URL,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (label === \"newUserCallbackURL\") {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tBASE_ERROR_CODES.INVALID_NEW_USER_CALLBACK_URL,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow APIError.fromStatus(\"FORBIDDEN\", {\n\t\t\t\tmessage: `Invalid ${label}`,\n\t\t\t});\n\t\t}\n\t};\n\n\tcallbackURL && validateURL(callbackURL, \"callbackURL\");\n\tredirectURL && validateURL(redirectURL, \"redirectURL\");\n\terrorCallbackURL && validateURL(errorCallbackURL, \"errorCallbackURL\");\n\tnewUserCallbackURL && validateURL(newUserCallbackURL, \"newUserCallbackURL\");\n});\n\nexport const originCheck = (\n\tgetValue: (ctx: GenericEndpointContext) => string | string[],\n) =>\n\tcreateAuthMiddleware(async (ctx) => {\n\t\tif (!ctx.request) {\n\t\t\treturn;\n\t\t}\n\t\tif (shouldSkipOriginCheck(ctx)) {\n\t\t\treturn;\n\t\t}\n\t\tconst callbackURL = getValue(ctx);\n\t\tconst validateURL = (url: string | undefined, label: string) => {\n\t\t\tif (!url) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst isTrustedOrigin = ctx.context.isTrustedOrigin(url, {\n\t\t\t\tallowRelativePaths: label !== \"origin\",\n\t\t\t});\n\n\t\t\tif (!isTrustedOrigin) {\n\t\t\t\tctx.context.logger.error(`Invalid ${label}: ${url}`);\n\t\t\t\tctx.context.logger.info(\n\t\t\t\t\t`If it's a valid URL, please add ${url} to trustedOrigins in your auth config\\n`,\n\t\t\t\t\t`Current list of trustedOrigins: ${ctx.context.trustedOrigins}`,\n\t\t\t\t);\n\t\t\t\tif (label === \"origin\") {\n\t\t\t\t\tthrow APIError.from(\"FORBIDDEN\", BASE_ERROR_CODES.INVALID_ORIGIN);\n\t\t\t\t}\n\t\t\t\tif (label === \"callbackURL\") {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tBASE_ERROR_CODES.INVALID_CALLBACK_URL,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (label === \"redirectURL\") {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tBASE_ERROR_CODES.INVALID_REDIRECT_URL,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (label === \"errorCallbackURL\") {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tBASE_ERROR_CODES.INVALID_ERROR_CALLBACK_URL,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (label === \"newUserCallbackURL\") {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tBASE_ERROR_CODES.INVALID_NEW_USER_CALLBACK_URL,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow APIError.fromStatus(\"FORBIDDEN\", {\n\t\t\t\t\tmessage: `Invalid ${label}`,\n\t\t\t\t});\n\t\t\t}\n\t\t};\n\t\tconst callbacks = Array.isArray(callbackURL) ? callbackURL : [callbackURL];\n\t\tfor (const url of callbacks) {\n\t\t\tvalidateURL(url, \"callbackURL\");\n\t\t}\n\t});\n\n/**\n * Validates origin header against trusted origins.\n * @param ctx - The endpoint context\n * @param forceValidate - If true, always validate origin regardless of cookies/skip flags\n */\nasync function validateOrigin(\n\tctx: GenericEndpointContext,\n\tforceValidate = false,\n): Promise<void> {\n\tconst headers = ctx.request?.headers;\n\tif (!headers || !ctx.request) {\n\t\treturn;\n\t}\n\tconst originHeader = headers.get(\"origin\") || headers.get(\"referer\") || \"\";\n\tconst useCookies = headers.has(\"cookie\");\n\n\tif (ctx.context.skipCSRFCheck) {\n\t\treturn;\n\t}\n\n\tif (shouldSkipCSRFForBackwardCompat(ctx)) {\n\t\tctx.context.options.advanced?.disableOriginCheck === true &&\n\t\t\tlogBackwardCompatWarning();\n\t\treturn;\n\t}\n\n\tif (shouldSkipOriginCheck(ctx)) {\n\t\treturn;\n\t}\n\n\tconst shouldValidate = forceValidate || useCookies;\n\n\tif (!shouldValidate) {\n\t\treturn;\n\t}\n\n\tif (!originHeader || originHeader === \"null\") {\n\t\tthrow APIError.from(\"FORBIDDEN\", BASE_ERROR_CODES.MISSING_OR_NULL_ORIGIN);\n\t}\n\n\tconst trustedOrigins: string[] = Array.isArray(\n\t\tctx.context.options.trustedOrigins,\n\t)\n\t\t? ctx.context.trustedOrigins\n\t\t: [\n\t\t\t\t...ctx.context.trustedOrigins,\n\t\t\t\t...((await ctx.context.options.trustedOrigins?.(ctx.request))?.filter(\n\t\t\t\t\t(v): v is string => Boolean(v),\n\t\t\t\t) || []),\n\t\t\t];\n\n\tconst isTrustedOrigin = trustedOrigins.some((origin) =>\n\t\tmatchesOriginPattern(originHeader, origin),\n\t);\n\tif (!isTrustedOrigin) {\n\t\tctx.context.logger.error(`Invalid origin: ${originHeader}`);\n\t\tctx.context.logger.info(\n\t\t\t`If it's a valid URL, please add ${originHeader} to trustedOrigins in your auth config\\n`,\n\t\t\t`Current list of trustedOrigins: ${trustedOrigins}`,\n\t\t);\n\t\tthrow APIError.from(\"FORBIDDEN\", BASE_ERROR_CODES.INVALID_ORIGIN);\n\t}\n}\n\n/**\n * Middleware for CSRF protection using Fetch Metadata headers.\n * This prevents cross-site navigation login attacks while supporting progressive enhancement.\n */\nexport const formCsrfMiddleware = createAuthMiddleware(async (ctx) => {\n\tconst request = ctx.request;\n\tif (!request) {\n\t\treturn;\n\t}\n\n\tawait validateFormCsrf(ctx);\n});\n\n/**\n * Validates CSRF protection for first-login scenarios using Fetch Metadata headers.\n * This prevents cross-site form submission attacks while supporting progressive enhancement.\n */\nasync function validateFormCsrf(ctx: GenericEndpointContext): Promise<void> {\n\tconst req = ctx.request;\n\tif (!req) {\n\t\treturn;\n\t}\n\n\tif (ctx.context.skipCSRFCheck) {\n\t\treturn;\n\t}\n\n\tif (shouldSkipCSRFForBackwardCompat(ctx)) {\n\t\treturn;\n\t}\n\n\tconst headers = req.headers;\n\tconst hasAnyCookies = headers.has(\"cookie\");\n\n\tif (hasAnyCookies) {\n\t\treturn await validateOrigin(ctx);\n\t}\n\n\tconst site = headers.get(\"Sec-Fetch-Site\");\n\tconst mode = headers.get(\"Sec-Fetch-Mode\");\n\tconst dest = headers.get(\"Sec-Fetch-Dest\");\n\n\tconst hasMetadata = Boolean(\n\t\t(site && site.trim()) || (mode && mode.trim()) || (dest && dest.trim()),\n\t);\n\n\tif (hasMetadata) {\n\t\t// Block cross-site navigation requests (classic CSRF attack pattern)\n\t\tif (site === \"cross-site\" && mode === \"navigate\") {\n\t\t\tctx.context.logger.error(\n\t\t\t\t\"Blocked cross-site navigation login attempt (CSRF protection)\",\n\t\t\t\t{\n\t\t\t\t\tsecFetchSite: site,\n\t\t\t\t\tsecFetchMode: mode,\n\t\t\t\t\tsecFetchDest: dest,\n\t\t\t\t},\n\t\t\t);\n\t\t\tthrow APIError.from(\n\t\t\t\t\"FORBIDDEN\",\n\t\t\t\tBASE_ERROR_CODES.CROSS_SITE_NAVIGATION_LOGIN_BLOCKED,\n\t\t\t);\n\t\t}\n\n\t\treturn await validateOrigin(ctx, true);\n\t}\n\n\t// No cookies, no Fetch Metadata → fallback to old behavior (no validation)\n\treturn;\n}\n"],"mappings":"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"}
@@ -92,7 +92,7 @@ declare const linkSocialAccount: better_call0.StrictEndpoint<"/link-social", {
92
92
  requireHeaders: true;
93
93
  body: z.ZodObject<{
94
94
  callbackURL: z.ZodOptional<z.ZodString>;
95
- provider: z.ZodType<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel", unknown, z.core.$ZodTypeInternals<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel", unknown>>;
95
+ provider: z.ZodType<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat", unknown, z.core.$ZodTypeInternals<(string & {}) | "linear" | "huggingface" | "github" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat", unknown>>;
96
96
  idToken: z.ZodOptional<z.ZodObject<{
97
97
  token: z.ZodString;
98
98
  nonce: z.ZodOptional<z.ZodString>;
@@ -1,5 +1,4 @@
1
1
  import { User } from "../../types/models.mjs";
2
- import "../../types/index.mjs";
3
2
  import { GenericEndpointContext } from "@better-auth/core";
4
3
  import * as better_call0 from "better-call";
5
4
  import * as z from "zod";
@@ -34,6 +34,7 @@ declare const requestPasswordReset: better_call0.StrictEndpoint<"/request-passwo
34
34
  };
35
35
  };
36
36
  };
37
+ use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>)[];
37
38
  }, {
38
39
  status: boolean;
39
40
  message: string;
@@ -38,7 +38,8 @@ const requestPasswordReset = createAuthEndpoint("/request-password-reset", {
38
38
  }
39
39
  } } }
40
40
  } }
41
- } }
41
+ } },
42
+ use: [originCheck((ctx) => ctx.body.redirectTo)]
42
43
  }, async (ctx) => {
43
44
  if (!ctx.context.options.emailAndPassword?.sendResetPassword) {
44
45
  ctx.context.logger.error("Reset password isn't enabled.Please pass an emailAndPassword.sendResetPassword function in your auth config!");