better-auth 1.5.5 → 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.
- package/dist/api/index.d.mts +6 -10
- package/dist/api/index.mjs +19 -4
- package/dist/api/index.mjs.map +1 -1
- package/dist/api/middlewares/origin-check.mjs +17 -8
- package/dist/api/middlewares/origin-check.mjs.map +1 -1
- package/dist/api/routes/account.d.mts +1 -1
- package/dist/api/routes/email-verification.d.mts +0 -1
- package/dist/api/routes/session.d.mts +0 -1
- package/dist/api/routes/sign-in.d.mts +2 -2
- package/dist/api/routes/sign-up.d.mts +0 -1
- package/dist/api/routes/update-session.d.mts +0 -1
- package/dist/api/routes/update-user.d.mts +0 -1
- package/dist/api/to-auth-endpoints.mjs +49 -12
- package/dist/api/to-auth-endpoints.mjs.map +1 -1
- package/dist/auth/full.d.mts +0 -1
- package/dist/auth/minimal.d.mts +0 -1
- package/dist/client/index.d.mts +0 -2
- package/dist/client/path-to-object.d.mts +3 -1
- package/dist/client/session-refresh.d.mts +0 -1
- package/dist/client/session-refresh.mjs +12 -4
- package/dist/client/session-refresh.mjs.map +1 -1
- package/dist/client/types.d.mts +0 -1
- package/dist/context/create-context.mjs +4 -1
- package/dist/context/create-context.mjs.map +1 -1
- package/dist/context/helpers.mjs +10 -4
- package/dist/context/helpers.mjs.map +1 -1
- package/dist/cookies/index.d.mts +0 -1
- package/dist/cookies/session-store.d.mts +0 -2
- package/dist/db/index.d.mts +2 -2
- package/dist/db/internal-adapter.d.mts +2 -1
- package/dist/db/internal-adapter.mjs +1 -1
- package/dist/db/internal-adapter.mjs.map +1 -1
- package/dist/db/schema.d.mts +0 -1
- package/dist/db/with-hooks.d.mts +6 -2
- package/dist/db/with-hooks.mjs +72 -31
- package/dist/db/with-hooks.mjs.map +1 -1
- package/dist/index.d.mts +0 -2
- package/dist/integrations/node.d.mts +0 -1
- package/dist/oauth2/link-account.d.mts +0 -1
- package/dist/plugins/admin/access/statement.d.mts +0 -2
- package/dist/plugins/admin/admin.d.mts +0 -1
- package/dist/plugins/admin/client.d.mts +0 -2
- package/dist/plugins/admin/types.d.mts +0 -2
- package/dist/plugins/anonymous/types.d.mts +0 -1
- package/dist/plugins/email-otp/index.mjs +2 -1
- package/dist/plugins/email-otp/index.mjs.map +1 -1
- package/dist/plugins/email-otp/otp-token.mjs +31 -2
- package/dist/plugins/email-otp/otp-token.mjs.map +1 -1
- package/dist/plugins/email-otp/routes.mjs +60 -59
- package/dist/plugins/email-otp/routes.mjs.map +1 -1
- package/dist/plugins/email-otp/types.d.mts +12 -0
- package/dist/plugins/email-otp/utils.mjs +4 -1
- package/dist/plugins/email-otp/utils.mjs.map +1 -1
- package/dist/plugins/generic-oauth/client.d.mts +0 -1
- package/dist/plugins/generic-oauth/index.d.mts +0 -1
- package/dist/plugins/index.d.mts +0 -3
- package/dist/plugins/jwt/types.d.mts +0 -1
- package/dist/plugins/magic-link/index.d.mts +2 -0
- package/dist/plugins/magic-link/index.mjs +5 -3
- package/dist/plugins/magic-link/index.mjs.map +1 -1
- package/dist/plugins/mcp/index.d.mts +0 -1
- package/dist/plugins/oidc-provider/index.d.mts +0 -1
- package/dist/plugins/oidc-provider/types.d.mts +0 -1
- package/dist/plugins/one-time-token/index.d.mts +0 -1
- package/dist/plugins/organization/access/statement.d.mts +0 -2
- package/dist/plugins/organization/adapter.d.mts +0 -2
- package/dist/plugins/organization/adapter.mjs +2 -2
- package/dist/plugins/organization/adapter.mjs.map +1 -1
- package/dist/plugins/organization/client.d.mts +0 -5
- package/dist/plugins/organization/organization.d.mts +0 -2
- package/dist/plugins/organization/permission.d.mts +0 -1
- package/dist/plugins/organization/routes/crud-access-control.d.mts +0 -2
- package/dist/plugins/organization/routes/crud-invites.d.mts +0 -3
- package/dist/plugins/organization/routes/crud-members.d.mts +0 -3
- package/dist/plugins/organization/routes/crud-org.d.mts +0 -3
- package/dist/plugins/organization/routes/crud-team.d.mts +2 -3
- package/dist/plugins/organization/routes/crud-team.mjs +18 -14
- package/dist/plugins/organization/routes/crud-team.mjs.map +1 -1
- package/dist/plugins/organization/schema.d.mts +0 -1
- package/dist/plugins/organization/types.d.mts +0 -2
- package/dist/plugins/phone-number/types.d.mts +0 -1
- package/dist/plugins/siwe/index.d.mts +0 -1
- package/dist/plugins/test-utils/types.d.mts +0 -2
- package/dist/plugins/two-factor/client.d.mts +7 -0
- package/dist/plugins/two-factor/client.mjs +5 -1
- package/dist/plugins/two-factor/client.mjs.map +1 -1
- package/dist/plugins/two-factor/types.d.mts +0 -1
- package/dist/test-utils/test-instance.d.mts +18 -22
- package/dist/types/index.d.mts +0 -1
- package/package.json +13 -10
package/dist/api/index.d.mts
CHANGED
|
@@ -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;
|
|
@@ -121,7 +117,7 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
|
|
|
121
117
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
122
118
|
newUserCallbackURL: zod.ZodOptional<zod.ZodString>;
|
|
123
119
|
errorCallbackURL: zod.ZodOptional<zod.ZodString>;
|
|
124
|
-
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>>;
|
|
125
121
|
disableRedirect: zod.ZodOptional<zod.ZodBoolean>;
|
|
126
122
|
idToken: zod.ZodOptional<zod.ZodObject<{
|
|
127
123
|
token: zod.ZodString;
|
|
@@ -1573,7 +1569,7 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
|
|
|
1573
1569
|
requireHeaders: true;
|
|
1574
1570
|
body: zod.ZodObject<{
|
|
1575
1571
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
1576
|
-
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>>;
|
|
1577
1573
|
idToken: zod.ZodOptional<zod.ZodObject<{
|
|
1578
1574
|
token: zod.ZodString;
|
|
1579
1575
|
nonce: zod.ZodOptional<zod.ZodString>;
|
|
@@ -2080,7 +2076,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
|
|
|
2080
2076
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
2081
2077
|
newUserCallbackURL: zod.ZodOptional<zod.ZodString>;
|
|
2082
2078
|
errorCallbackURL: zod.ZodOptional<zod.ZodString>;
|
|
2083
|
-
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>>;
|
|
2084
2080
|
disableRedirect: zod.ZodOptional<zod.ZodBoolean>;
|
|
2085
2081
|
idToken: zod.ZodOptional<zod.ZodObject<{
|
|
2086
2082
|
token: zod.ZodString;
|
|
@@ -2107,7 +2103,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
|
|
|
2107
2103
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
2108
2104
|
newUserCallbackURL: zod.ZodOptional<zod.ZodString>;
|
|
2109
2105
|
errorCallbackURL: zod.ZodOptional<zod.ZodString>;
|
|
2110
|
-
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>>;
|
|
2111
2107
|
disableRedirect: zod.ZodOptional<zod.ZodBoolean>;
|
|
2112
2108
|
idToken: zod.ZodOptional<zod.ZodObject<{
|
|
2113
2109
|
token: zod.ZodString;
|
|
@@ -3559,7 +3555,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
|
|
|
3559
3555
|
requireHeaders: true;
|
|
3560
3556
|
body: zod.ZodObject<{
|
|
3561
3557
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
3562
|
-
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>>;
|
|
3563
3559
|
idToken: zod.ZodOptional<zod.ZodObject<{
|
|
3564
3560
|
token: zod.ZodString;
|
|
3565
3561
|
nonce: zod.ZodOptional<zod.ZodString>;
|
package/dist/api/index.mjs
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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;
|
package/dist/api/index.mjs.map
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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":"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"}
|
|
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,6 +1,5 @@
|
|
|
1
1
|
import { Prettify as Prettify$1 } from "../../types/helper.mjs";
|
|
2
2
|
import { Session as Session$1, User as User$1 } from "../../types/models.mjs";
|
|
3
|
-
import "../../types/index.mjs";
|
|
4
3
|
import { BetterAuthOptions, GenericEndpointContext } from "@better-auth/core";
|
|
5
4
|
import * as _better_auth_core_db0 from "@better-auth/core/db";
|
|
6
5
|
import * as better_call0 from "better-call";
|
|
@@ -8,7 +8,7 @@ declare const socialSignInBodySchema: z.ZodObject<{
|
|
|
8
8
|
callbackURL: z.ZodOptional<z.ZodString>;
|
|
9
9
|
newUserCallbackURL: z.ZodOptional<z.ZodString>;
|
|
10
10
|
errorCallbackURL: z.ZodOptional<z.ZodString>;
|
|
11
|
-
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>>;
|
|
11
|
+
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>>;
|
|
12
12
|
disableRedirect: z.ZodOptional<z.ZodBoolean>;
|
|
13
13
|
idToken: z.ZodOptional<z.ZodObject<{
|
|
14
14
|
token: z.ZodString;
|
|
@@ -36,7 +36,7 @@ declare const signInSocial: <O extends BetterAuthOptions>() => better_call0.Stri
|
|
|
36
36
|
callbackURL: z.ZodOptional<z.ZodString>;
|
|
37
37
|
newUserCallbackURL: z.ZodOptional<z.ZodString>;
|
|
38
38
|
errorCallbackURL: z.ZodOptional<z.ZodString>;
|
|
39
|
-
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>>;
|
|
39
|
+
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>>;
|
|
40
40
|
disableRedirect: z.ZodOptional<z.ZodBoolean>;
|
|
41
41
|
idToken: z.ZodOptional<z.ZodObject<{
|
|
42
42
|
token: z.ZodString;
|
|
@@ -3,6 +3,7 @@ import { hasRequestState, runWithEndpointContext, runWithRequestState } from "@b
|
|
|
3
3
|
import { shouldPublishLog } from "@better-auth/core/env";
|
|
4
4
|
import { APIError } from "@better-auth/core/error";
|
|
5
5
|
import { createDefu } from "defu";
|
|
6
|
+
import { ATTR_CONTEXT, ATTR_HOOK_TYPE, ATTR_HTTP_ROUTE, ATTR_OPERATION_ID, withSpan } from "@better-auth/core/instrumentation";
|
|
6
7
|
import { kAPIErrorHeaderSymbol, toResponse } from "better-call";
|
|
7
8
|
|
|
8
9
|
//#region src/api/to-auth-endpoints.ts
|
|
@@ -13,12 +14,22 @@ const defuReplaceArrays = createDefu((obj, key, value) => {
|
|
|
13
14
|
}
|
|
14
15
|
});
|
|
15
16
|
const hooksSourceWeakMap = /* @__PURE__ */ new WeakMap();
|
|
17
|
+
function getOperationId(endpoint, key) {
|
|
18
|
+
if (!endpoint?.options) return key;
|
|
19
|
+
const opts = endpoint.options;
|
|
20
|
+
return opts.operationId ?? opts.metadata?.openapi?.operationId ?? key;
|
|
21
|
+
}
|
|
16
22
|
function toAuthEndpoints(endpoints, ctx) {
|
|
17
23
|
const api = {};
|
|
18
24
|
for (const [key, endpoint] of Object.entries(endpoints)) {
|
|
19
25
|
api[key] = async (context) => {
|
|
26
|
+
const operationId = getOperationId(endpoint, key);
|
|
27
|
+
const endpointMethod = endpoint?.options?.method;
|
|
28
|
+
const defaultMethod = Array.isArray(endpointMethod) ? endpointMethod[0] : endpointMethod;
|
|
20
29
|
const run = async () => {
|
|
21
30
|
const authContext = await ctx;
|
|
31
|
+
const methodName = context?.method ?? context?.request?.method ?? defaultMethod ?? "?";
|
|
32
|
+
const pathName = context?.path ?? endpoint.path ?? "/:virtual";
|
|
22
33
|
let internalContext = {
|
|
23
34
|
...context,
|
|
24
35
|
context: {
|
|
@@ -30,9 +41,12 @@ function toAuthEndpoints(endpoints, ctx) {
|
|
|
30
41
|
path: endpoint.path,
|
|
31
42
|
headers: context?.headers ? new Headers(context?.headers) : void 0
|
|
32
43
|
};
|
|
33
|
-
return
|
|
44
|
+
return withSpan(`${methodName} ${pathName}`, {
|
|
45
|
+
[ATTR_HTTP_ROUTE]: pathName,
|
|
46
|
+
[ATTR_OPERATION_ID]: operationId
|
|
47
|
+
}, async () => runWithEndpointContext(internalContext, async () => {
|
|
34
48
|
const { beforeHooks, afterHooks } = getHooks(authContext);
|
|
35
|
-
const before = await runBeforeHooks(internalContext, beforeHooks);
|
|
49
|
+
const before = await runBeforeHooks(internalContext, beforeHooks, endpoint, operationId);
|
|
36
50
|
/**
|
|
37
51
|
* If `before.context` is returned, it should
|
|
38
52
|
* get merged with the original context
|
|
@@ -55,7 +69,10 @@ function toAuthEndpoints(endpoints, ctx) {
|
|
|
55
69
|
internalContext.asResponse = false;
|
|
56
70
|
internalContext.returnHeaders = true;
|
|
57
71
|
internalContext.returnStatus = true;
|
|
58
|
-
const result = await runWithEndpointContext(internalContext, () =>
|
|
72
|
+
const result = await runWithEndpointContext(internalContext, () => withSpan(`handler ${pathName}`, {
|
|
73
|
+
[ATTR_HTTP_ROUTE]: pathName,
|
|
74
|
+
[ATTR_OPERATION_ID]: operationId
|
|
75
|
+
}, () => endpoint(internalContext))).catch((e) => {
|
|
59
76
|
if (isAPIError(e))
|
|
60
77
|
/**
|
|
61
78
|
* API Errors from response are caught
|
|
@@ -71,7 +88,7 @@ function toAuthEndpoints(endpoints, ctx) {
|
|
|
71
88
|
if (result && result instanceof Response) return result;
|
|
72
89
|
internalContext.context.returned = result.response;
|
|
73
90
|
internalContext.context.responseHeaders = result.headers;
|
|
74
|
-
const after = await runAfterHooks(internalContext, afterHooks);
|
|
91
|
+
const after = await runAfterHooks(internalContext, afterHooks, endpoint, operationId);
|
|
75
92
|
if (after.response) result.response = after.response;
|
|
76
93
|
if (isAPIError(result.response) && shouldPublishLog(authContext.logger.level, "debug")) result.response.stack = result.response.errorStack;
|
|
77
94
|
if (isAPIError(result.response) && !context?.asResponse) throw result.response;
|
|
@@ -89,7 +106,7 @@ function toAuthEndpoints(endpoints, ctx) {
|
|
|
89
106
|
response: result.response,
|
|
90
107
|
status: result.status
|
|
91
108
|
} : result.response;
|
|
92
|
-
});
|
|
109
|
+
}));
|
|
93
110
|
};
|
|
94
111
|
if (await hasRequestState()) return run();
|
|
95
112
|
else return runWithRequestState(/* @__PURE__ */ new WeakMap(), run);
|
|
@@ -99,7 +116,7 @@ function toAuthEndpoints(endpoints, ctx) {
|
|
|
99
116
|
}
|
|
100
117
|
return api;
|
|
101
118
|
}
|
|
102
|
-
async function runBeforeHooks(context, hooks) {
|
|
119
|
+
async function runBeforeHooks(context, hooks, endpoint, operationId) {
|
|
103
120
|
let modifiedContext = {};
|
|
104
121
|
for (const hook of hooks) {
|
|
105
122
|
let matched = false;
|
|
@@ -111,10 +128,17 @@ async function runBeforeHooks(context, hooks) {
|
|
|
111
128
|
throw new APIError("INTERNAL_SERVER_ERROR", { message: `An error occurred during hook matcher execution. Check the logs for more details.` });
|
|
112
129
|
}
|
|
113
130
|
if (matched) {
|
|
114
|
-
const
|
|
131
|
+
const hookSource = hooksSourceWeakMap.get(hook.handler) ?? "unknown";
|
|
132
|
+
const path = context.path ?? endpoint?.path ?? "/:virtual";
|
|
133
|
+
const result = await withSpan(`hook before ${path} ${hookSource}`, {
|
|
134
|
+
[ATTR_HOOK_TYPE]: "before",
|
|
135
|
+
[ATTR_HTTP_ROUTE]: path,
|
|
136
|
+
[ATTR_CONTEXT]: hookSource,
|
|
137
|
+
[ATTR_OPERATION_ID]: operationId
|
|
138
|
+
}, () => hook.handler({
|
|
115
139
|
...context,
|
|
116
140
|
returnHeaders: false
|
|
117
|
-
}).catch((e) => {
|
|
141
|
+
})).catch((e) => {
|
|
118
142
|
if (isAPIError(e) && shouldPublishLog(context.context.logger.level, "debug")) e.stack = e.errorStack;
|
|
119
143
|
throw e;
|
|
120
144
|
});
|
|
@@ -134,9 +158,16 @@ async function runBeforeHooks(context, hooks) {
|
|
|
134
158
|
}
|
|
135
159
|
return { context: modifiedContext };
|
|
136
160
|
}
|
|
137
|
-
async function runAfterHooks(context, hooks) {
|
|
161
|
+
async function runAfterHooks(context, hooks, endpoint, operationId) {
|
|
138
162
|
for (const hook of hooks) if (hook.matcher(context)) {
|
|
139
|
-
const
|
|
163
|
+
const hookSource = hooksSourceWeakMap.get(hook.handler) ?? "unknown";
|
|
164
|
+
const path = context.path ?? endpoint?.path ?? "/:virtual";
|
|
165
|
+
const result = await withSpan(`hook after ${path} ${hookSource}`, {
|
|
166
|
+
[ATTR_HOOK_TYPE]: "after",
|
|
167
|
+
[ATTR_HTTP_ROUTE]: path,
|
|
168
|
+
[ATTR_CONTEXT]: hookSource,
|
|
169
|
+
[ATTR_OPERATION_ID]: operationId
|
|
170
|
+
}, () => hook.handler(context)).catch((e) => {
|
|
140
171
|
if (isAPIError(e)) {
|
|
141
172
|
const headers = e[kAPIErrorHeaderSymbol];
|
|
142
173
|
if (shouldPublishLog(context.context.logger.level, "debug")) e.stack = e.errorStack;
|
|
@@ -179,8 +210,14 @@ function getHooks(authContext) {
|
|
|
179
210
|
handler: afterHookHandler
|
|
180
211
|
});
|
|
181
212
|
}
|
|
182
|
-
const pluginBeforeHooks = plugins.
|
|
183
|
-
|
|
213
|
+
const pluginBeforeHooks = plugins.flatMap((plugin) => (plugin.hooks?.before ?? []).map((h) => {
|
|
214
|
+
hooksSourceWeakMap.set(h.handler, `plugin:${plugin.id}`);
|
|
215
|
+
return h;
|
|
216
|
+
}));
|
|
217
|
+
const pluginAfterHooks = plugins.flatMap((plugin) => (plugin.hooks?.after ?? []).map((h) => {
|
|
218
|
+
hooksSourceWeakMap.set(h.handler, `plugin:${plugin.id}`);
|
|
219
|
+
return h;
|
|
220
|
+
}));
|
|
184
221
|
/**
|
|
185
222
|
* Add plugin added hooks at last
|
|
186
223
|
*/
|