fix: remove current url from requests (#1187)

This commit is contained in:
Bereket Engida
2025-01-11 22:05:43 +03:00
committed by GitHub
parent 4326e191a4
commit 3aa95aa125
15 changed files with 15 additions and 149 deletions

View File

@@ -6,6 +6,7 @@
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"declaration": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",

View File

@@ -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",

View File

@@ -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");
});

View File

@@ -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.

View File

@@ -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({

View File

@@ -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,

View File

@@ -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

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,
],

View File

@@ -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;

View File

@@ -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";

View File

@@ -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,
/**

View File

@@ -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",

View File

@@ -736,3 +736,4 @@ export const oidcProvider = (options: OIDCOptions) => {
schema,
} satisfies BetterAuthPlugin;
};
export type * from "./types";