diff --git a/docs/content/docs/concepts/database.mdx b/docs/content/docs/concepts/database.mdx index 54b368479b..4cf4e8725a 100644 --- a/docs/content/docs/concepts/database.mdx +++ b/docs/content/docs/concepts/database.mdx @@ -234,7 +234,7 @@ export const auth = betterAuth({ secondaryStorage: { get: async (key) => { const value = await redis.get(key); - return value ? value : null; + return value ? JSON.stringify(value) : null; }, set: async (key, value, ttl) => { if (ttl) await redis.set(key, value, { EX: ttl }); diff --git a/packages/better-auth/src/api/middlewares/origin-check.test.ts b/packages/better-auth/src/api/middlewares/origin-check.test.ts index eabd1807ff..47754fba54 100644 --- a/packages/better-auth/src/api/middlewares/origin-check.test.ts +++ b/packages/better-auth/src/api/middlewares/origin-check.test.ts @@ -187,6 +187,24 @@ describe("Origin Check", async (it) => { expect(res.data?.user).toBeDefined(); }); + it("shouldn't work with callback url with double slash", async (ctx) => { + const client = createAuthClient({ + baseURL: "http://localhost:3000", + fetchOptions: { + customFetchImpl, + headers: { + origin: "https://localhost:3000", + }, + }, + }); + const res = await client.signIn.email({ + email: testUser.email, + password: testUser.password, + callbackURL: "//evil.com", + }); + expect(res.error?.status).toBe(403); + }); + it("should work with GET requests", async (ctx) => { const client = createAuthClient({ baseURL: "https://sub-domain.my-site.com", diff --git a/packages/better-auth/src/api/middlewares/origin-check.ts b/packages/better-auth/src/api/middlewares/origin-check.ts index 08ed84a998..6d0a85370a 100644 --- a/packages/better-auth/src/api/middlewares/origin-check.ts +++ b/packages/better-auth/src/api/middlewares/origin-check.ts @@ -47,7 +47,10 @@ export const originCheckMiddleware = createAuthMiddleware(async (ctx) => { const isTrustedOrigin = trustedOrigins.some( (origin) => matchesPattern(url, origin) || - (url?.startsWith("/") && label !== "origin" && !url.includes(":")), + (url?.startsWith("/") && + label !== "origin" && + !url.includes(":") && + !url.includes("//")), ); if (!isTrustedOrigin) { ctx.context.logger.error(`Invalid ${label}: ${url}`); @@ -102,7 +105,10 @@ export const originCheck = ( const isTrustedOrigin = trustedOrigins.some( (origin) => matchesPattern(url, origin) || - (url?.startsWith("/") && label !== "origin" && !url.includes(":")), + (url?.startsWith("/") && + label !== "origin" && + !url.includes(":") && + !url.includes("//")), ); if (!isTrustedOrigin) { ctx.context.logger.error(`Invalid ${label}: ${url}`); diff --git a/packages/better-auth/src/api/routes/session.ts b/packages/better-auth/src/api/routes/session.ts index 15e2c6e541..7f995ce936 100644 --- a/packages/better-auth/src/api/routes/session.ts +++ b/packages/better-auth/src/api/routes/session.ts @@ -118,7 +118,10 @@ export const getSession =