mirror of
https://github.com/better-auth/better-auth.git
synced 2026-06-01 20:06:41 -05:00
fix: remove current url from requests (#1187)
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"declaration": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
|
||||
@@ -126,28 +126,6 @@ describe("Origin Check", async (it) => {
|
||||
expect(res.data?.user).toBeDefined();
|
||||
});
|
||||
|
||||
it("shouldn't allow untrusted currentURL", async (ctx) => {
|
||||
const client = createAuthClient({
|
||||
baseURL: "http://localhost:3000",
|
||||
fetchOptions: {
|
||||
customFetchImpl,
|
||||
},
|
||||
});
|
||||
|
||||
const res2 = await client.signIn.email({
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
fetchOptions: {
|
||||
// @ts-expect-error - query is not defined in the type
|
||||
query: {
|
||||
currentURL: "http://malicious.com",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(res2.error?.status).toBe(403);
|
||||
expect(res2.error?.message).toBe("Invalid currentURL");
|
||||
});
|
||||
|
||||
it("shouldn't allow untrusted redirectTo", async (ctx) => {
|
||||
const client = createAuthClient({
|
||||
baseURL: "http://localhost:3000",
|
||||
@@ -163,35 +141,6 @@ describe("Origin Check", async (it) => {
|
||||
expect(res.error?.message).toBe("Invalid redirectURL");
|
||||
});
|
||||
|
||||
it("should work with list of trusted origins ", async (ctx) => {
|
||||
const client = createAuthClient({
|
||||
baseURL: "http://localhost:3000",
|
||||
fetchOptions: {
|
||||
customFetchImpl,
|
||||
headers: {
|
||||
origin: "https://trusted.com",
|
||||
},
|
||||
},
|
||||
});
|
||||
const res = await client.forgetPassword({
|
||||
email: testUser.email,
|
||||
redirectTo: "http://localhost:5000/reset-password",
|
||||
});
|
||||
expect(res.data?.status).toBeTruthy();
|
||||
|
||||
const res2 = await client.signIn.email({
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
fetchOptions: {
|
||||
// @ts-expect-error - query is not defined in the type
|
||||
query: {
|
||||
currentURL: "http://localhost:5000",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(res2.data?.user).toBeDefined();
|
||||
});
|
||||
|
||||
it("should work with wildcard trusted origins", async (ctx) => {
|
||||
const client = createAuthClient({
|
||||
baseURL: "https://sub-domain.my-site.com",
|
||||
|
||||
@@ -17,7 +17,6 @@ export const originCheckMiddleware = createAuthMiddleware(async (ctx) => {
|
||||
ctx.headers?.get("origin") || ctx.headers?.get("referer") || "";
|
||||
const callbackURL = body?.callbackURL || query?.callbackURL;
|
||||
const redirectURL = body?.redirectTo;
|
||||
const currentURL = query?.currentURL;
|
||||
const errorCallbackURL = body?.errorCallbackURL;
|
||||
const newUserCallbackURL = body?.newUserCallbackURL;
|
||||
const trustedOrigins = context.trustedOrigins;
|
||||
@@ -59,7 +58,6 @@ export const originCheckMiddleware = createAuthMiddleware(async (ctx) => {
|
||||
}
|
||||
callbackURL && validateURL(callbackURL, "callbackURL");
|
||||
redirectURL && validateURL(redirectURL, "redirectURL");
|
||||
currentURL && validateURL(currentURL, "currentURL");
|
||||
errorCallbackURL && validateURL(errorCallbackURL, "errorCallbackURL");
|
||||
newUserCallbackURL && validateURL(newUserCallbackURL, "newUserCallbackURL");
|
||||
});
|
||||
|
||||
@@ -65,15 +65,6 @@ export const linkSocialAccount = createAuthEndpoint(
|
||||
{
|
||||
method: "POST",
|
||||
requireHeaders: true,
|
||||
query: z
|
||||
.object({
|
||||
/**
|
||||
* Redirect to the current URL after the
|
||||
* user has signed in.
|
||||
*/
|
||||
currentURL: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
body: z.object({
|
||||
/**
|
||||
* Callback URL to redirect to after the user has signed in.
|
||||
|
||||
@@ -53,7 +53,7 @@ export async function sendVerificationEmailFn(
|
||||
ctx.context.options.emailVerification?.expiresIn,
|
||||
);
|
||||
const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${
|
||||
ctx.body.callbackURL || ctx.query?.currentURL || "/"
|
||||
ctx.body.callbackURL || "/"
|
||||
}`;
|
||||
await ctx.context.options.emailVerification.sendVerificationEmail(
|
||||
{
|
||||
@@ -69,15 +69,6 @@ export const sendVerificationEmail = createAuthEndpoint(
|
||||
"/send-verification-email",
|
||||
{
|
||||
method: "POST",
|
||||
query: z
|
||||
.object({
|
||||
currentURL: z
|
||||
.string({
|
||||
description: "The URL to use for email verification callback",
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
body: z.object({
|
||||
email: z
|
||||
.string({
|
||||
|
||||
@@ -195,13 +195,12 @@ export const forgetPasswordCallback = createAuthEndpoint(
|
||||
export const resetPassword = createAuthEndpoint(
|
||||
"/reset-password",
|
||||
{
|
||||
query: z.optional(
|
||||
z.object({
|
||||
token: z.string().optional(),
|
||||
currentURL: z.string().optional(),
|
||||
}),
|
||||
),
|
||||
method: "POST",
|
||||
query: z
|
||||
.object({
|
||||
token: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
body: z.object({
|
||||
newPassword: z.string({
|
||||
description: "The new password to set",
|
||||
@@ -236,12 +235,7 @@ export const resetPassword = createAuthEndpoint(
|
||||
},
|
||||
},
|
||||
async (ctx) => {
|
||||
const token =
|
||||
ctx.body.token ||
|
||||
ctx.query?.token ||
|
||||
(ctx.query?.currentURL
|
||||
? new URL(ctx.query.currentURL).searchParams.get("token")
|
||||
: "");
|
||||
const token = ctx.body.token || ctx.query?.token;
|
||||
if (!token) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: BASE_ERROR_CODES.INVALID_TOKEN,
|
||||
|
||||
@@ -12,15 +12,6 @@ export const signInSocial = createAuthEndpoint(
|
||||
"/sign-in/social",
|
||||
{
|
||||
method: "POST",
|
||||
query: z
|
||||
.object({
|
||||
/**
|
||||
* Redirect to the current URL after the
|
||||
* user has signed in.
|
||||
*/
|
||||
currentURL: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
body: z.object({
|
||||
/**
|
||||
* Callback URL to redirect to after the user
|
||||
|
||||
@@ -18,11 +18,6 @@ export const signUpEmail = <O extends BetterAuthOptions>() =>
|
||||
"/sign-up/email",
|
||||
{
|
||||
method: "POST",
|
||||
query: z
|
||||
.object({
|
||||
currentURL: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
body: z.record(z.string(), z.any()) as unknown as ZodObject<{
|
||||
name: ZodString;
|
||||
email: ZodString;
|
||||
@@ -197,9 +192,7 @@ export const signUpEmail = <O extends BetterAuthOptions>() =>
|
||||
);
|
||||
const url = `${
|
||||
ctx.context.baseURL
|
||||
}/verify-email?token=${token}&callbackURL=${
|
||||
body.callbackURL || ctx.query?.currentURL || "/"
|
||||
}`;
|
||||
}/verify-email?token=${token}&callbackURL=${body.callbackURL || "/"}`;
|
||||
await ctx.context.options.emailVerification?.sendVerificationEmail?.(
|
||||
{
|
||||
user: createdUser,
|
||||
|
||||
@@ -517,11 +517,6 @@ export const changeEmail = createAuthEndpoint(
|
||||
"/change-email",
|
||||
{
|
||||
method: "POST",
|
||||
query: z
|
||||
.object({
|
||||
currentURL: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
body: z.object({
|
||||
newEmail: z
|
||||
.string({
|
||||
@@ -616,9 +611,7 @@ export const changeEmail = createAuthEndpoint(
|
||||
);
|
||||
const url = `${
|
||||
ctx.context.baseURL
|
||||
}/verify-email?token=${token}&callbackURL=${
|
||||
ctx.body.callbackURL || ctx.query?.currentURL || "/"
|
||||
}`;
|
||||
}/verify-email?token=${token}&callbackURL=${ctx.body.callbackURL || "/"}`;
|
||||
await ctx.context.options.user.changeEmail.sendChangeEmailVerification(
|
||||
{
|
||||
user: ctx.context.session.user,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createFetch } from "@better-fetch/fetch";
|
||||
import { getBaseURL } from "../utils/url";
|
||||
import { type WritableAtom } from "nanostores";
|
||||
import type { AtomListener, ClientOptions } from "./types";
|
||||
import { addCurrentURL, redirectPlugin } from "./fetch-plugins";
|
||||
import { redirectPlugin } from "./fetch-plugins";
|
||||
import { getSessionAtom } from "./session-atom";
|
||||
import { parseJSON } from "./parser";
|
||||
|
||||
@@ -35,7 +35,6 @@ export const getClientConfig = (options?: ClientOptions) => {
|
||||
? [...(options?.fetchOptions?.plugins || []), ...pluginsFetchPlugins]
|
||||
: [
|
||||
redirectPlugin,
|
||||
addCurrentURL,
|
||||
...(options?.fetchOptions?.plugins || []),
|
||||
...pluginsFetchPlugins,
|
||||
],
|
||||
|
||||
@@ -17,22 +17,3 @@ export const redirectPlugin = {
|
||||
},
|
||||
},
|
||||
} satisfies BetterFetchPlugin;
|
||||
|
||||
export const addCurrentURL = {
|
||||
id: "add-current-url",
|
||||
name: "Add current URL",
|
||||
hooks: {
|
||||
onRequest(context) {
|
||||
if (typeof window !== "undefined" && window.location) {
|
||||
if (window.location) {
|
||||
try {
|
||||
const url = new URL(context.url);
|
||||
url.searchParams.set("currentURL", window.location.href);
|
||||
context.url = url;
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
return context;
|
||||
},
|
||||
},
|
||||
} satisfies BetterFetchPlugin;
|
||||
|
||||
@@ -16,3 +16,4 @@ export * from "../../plugins/custom-session/client";
|
||||
export * from "./infer-plugin";
|
||||
export * from "../../plugins/sso/client";
|
||||
export * from "../../plugins/oidc-provider/client";
|
||||
export type * from "@simplewebauthn/server";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { z } from "zod";
|
||||
import type { GenericEndpointContext } from "../types";
|
||||
import { APIError } from "better-call";
|
||||
import { getOrigin } from "../utils/url";
|
||||
import { generateRandomString } from "../crypto";
|
||||
|
||||
export async function generateState(
|
||||
@@ -11,10 +10,7 @@ export async function generateState(
|
||||
userId: string;
|
||||
},
|
||||
) {
|
||||
const callbackURL =
|
||||
c.body?.callbackURL ||
|
||||
(c.query?.currentURL ? getOrigin(c.query?.currentURL) : "") ||
|
||||
c.context.options.baseURL;
|
||||
const callbackURL = c.body?.callbackURL || c.context.options.baseURL;
|
||||
if (!callbackURL) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: "callbackURL is required",
|
||||
@@ -25,7 +21,7 @@ export async function generateState(
|
||||
const data = JSON.stringify({
|
||||
callbackURL,
|
||||
codeVerifier,
|
||||
errorURL: c.body?.errorCallbackURL || c.query?.currentURL,
|
||||
errorURL: c.body?.errorCallbackURL,
|
||||
newUserURL: c.body?.newUserCallbackURL,
|
||||
link,
|
||||
/**
|
||||
|
||||
@@ -260,19 +260,6 @@ export const genericOAuth = (options: GenericOAuthOptions) => {
|
||||
"/sign-in/oauth2",
|
||||
{
|
||||
method: "POST",
|
||||
query: z
|
||||
.object({
|
||||
/**
|
||||
* Redirect to the current URL after the
|
||||
* user has signed in.
|
||||
*/
|
||||
currentURL: z
|
||||
.string({
|
||||
description: "Redirect to the current URL after sign in",
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
body: z.object({
|
||||
providerId: z.string({
|
||||
description: "The provider ID for the OAuth provider",
|
||||
|
||||
@@ -736,3 +736,4 @@ export const oidcProvider = (options: OIDCOptions) => {
|
||||
schema,
|
||||
} satisfies BetterAuthPlugin;
|
||||
};
|
||||
export type * from "./types";
|
||||
|
||||
Reference in New Issue
Block a user