diff --git a/docs/content/docs/reference/options.mdx b/docs/content/docs/reference/options.mdx index 1b34eb2159..27c782950b 100644 --- a/docs/content/docs/reference/options.mdx +++ b/docs/content/docs/reference/options.mdx @@ -543,7 +543,8 @@ export const auth = betterAuth({ })) | false | "serial" | "uuid", defaultFindManyLimit: 100, experimentalJoins: false, - } + }, + skipTrailingSlashes: true }, }) ``` @@ -557,6 +558,7 @@ export const auth = betterAuth({ - `defaultCookieAttributes`: Default attributes for all cookies - `cookiePrefix`: Prefix for cookies - `database`: Database configuration options +- `skipTrailingSlashes`: Skip trailing slash validation in route matching. (default: `false`) - OAuth state configuration options (`storeStateStrategy`, `skipStateCookieCheck`) are now part of the `account` option ## `logger` diff --git a/packages/better-auth/src/api/index.test.ts b/packages/better-auth/src/api/index.test.ts index ac2f83c659..87f5a76416 100644 --- a/packages/better-auth/src/api/index.test.ts +++ b/packages/better-auth/src/api/index.test.ts @@ -183,3 +183,59 @@ describe("onRequest chain", () => { expect(result.error?.status).toBe(403); }); }); + +describe("skipTrailingSlashes option", () => { + it("should return 404 for trailing slash requests by default", async () => { + const { auth } = await getTestInstance({}); + + const response = await auth.handler( + new Request("http://localhost:3000/api/auth/ok/", { + method: "GET", + }), + ); + + expect(response.status).toBe(404); + }); + + it("should handle trailing slash requests when skipTrailingSlashes is enabled", async () => { + const { auth } = await getTestInstance({ + advanced: { + skipTrailingSlashes: true, + }, + }); + + const response = await auth.handler( + new Request("http://localhost:3000/api/auth/ok/", { + method: "GET", + }), + ); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual({ ok: true }); + }); + + it("should work with POST requests with trailing slash", async () => { + const { auth } = await getTestInstance({ + advanced: { + skipTrailingSlashes: true, + }, + }); + + // POST to sign-up endpoint with trailing slash + const response = await auth.handler( + new Request("http://localhost:3000/api/auth/sign-up/email/", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + email: "test2@example.com", + password: "password123", + name: "Test User 2", + }), + }), + ); + + // Should reach the endpoint (probably fail validation, but not 404) + expect(response.status).not.toBe(404); + }); +}); diff --git a/packages/better-auth/src/api/index.ts b/packages/better-auth/src/api/index.ts index d7142f50b7..1bf7255cc5 100644 --- a/packages/better-auth/src/api/index.ts +++ b/packages/better-auth/src/api/index.ts @@ -273,6 +273,7 @@ export const router =