mirror of
https://github.com/better-auth/better-auth.git
synced 2026-06-01 20:06:41 -05:00
fix: ignore cookiecache on auth sensitive functions (#4530)
This commit is contained in:
committed by
GitHub
parent
8eb64f4567
commit
e2a959eefe
@@ -302,6 +302,21 @@ export const sessionMiddleware = createAuthMiddleware(async (ctx) => {
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* This middleware forces the endpoint to require a valid session and ignores cookie cache.
|
||||
* This should be used for sensitive operations like password changes, account deletion, etc.
|
||||
* to ensure that revoked sessions cannot be used even if they're still cached in cookies.
|
||||
*/
|
||||
export const sensitiveSessionMiddleware = createAuthMiddleware(async (ctx) => {
|
||||
const session = await getSessionFromCtx(ctx, { disableCookieCache: true });
|
||||
if (!session?.session) {
|
||||
throw new APIError("UNAUTHORIZED");
|
||||
}
|
||||
return {
|
||||
session,
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* This middleware allows you to call the endpoint on the client if session is valid.
|
||||
* However, if called on the server, no session is required.
|
||||
@@ -408,7 +423,7 @@ export const revokeSession = createAuthEndpoint(
|
||||
description: "The token to revoke",
|
||||
}),
|
||||
}),
|
||||
use: [sessionMiddleware],
|
||||
use: [sensitiveSessionMiddleware],
|
||||
requireHeaders: true,
|
||||
metadata: {
|
||||
openapi: {
|
||||
@@ -486,7 +501,7 @@ export const revokeSessions = createAuthEndpoint(
|
||||
"/revoke-sessions",
|
||||
{
|
||||
method: "POST",
|
||||
use: [sessionMiddleware],
|
||||
use: [sensitiveSessionMiddleware],
|
||||
requireHeaders: true,
|
||||
metadata: {
|
||||
openapi: {
|
||||
@@ -539,7 +554,7 @@ export const revokeOtherSessions = createAuthEndpoint(
|
||||
{
|
||||
method: "POST",
|
||||
requireHeaders: true,
|
||||
use: [sessionMiddleware],
|
||||
use: [sensitiveSessionMiddleware],
|
||||
metadata: {
|
||||
openapi: {
|
||||
description:
|
||||
|
||||
@@ -475,4 +475,66 @@ describe("delete user", async () => {
|
||||
});
|
||||
expect(nullSession.data).toBeNull();
|
||||
});
|
||||
|
||||
it("should ignore cookie cache for sensitive operations like changePassword", async () => {
|
||||
const { client: cacheClient, sessionSetter: cacheSessionSetter } =
|
||||
await getTestInstance(
|
||||
{
|
||||
session: {
|
||||
cookieCache: {
|
||||
enabled: true,
|
||||
maxAge: 60,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
disableTestUser: true,
|
||||
},
|
||||
);
|
||||
|
||||
const uniqueEmail = `cache-test-${Date.now()}@test.com`;
|
||||
const testPassword = "testPassword123";
|
||||
|
||||
await cacheClient.signUp.email({
|
||||
email: uniqueEmail,
|
||||
password: testPassword,
|
||||
name: "Cache Test User",
|
||||
});
|
||||
|
||||
const cacheHeaders = new Headers();
|
||||
await cacheClient.signIn.email({
|
||||
email: uniqueEmail,
|
||||
password: testPassword,
|
||||
fetchOptions: {
|
||||
onSuccess: cacheSessionSetter(cacheHeaders),
|
||||
},
|
||||
});
|
||||
|
||||
const initialSession = await cacheClient.getSession({
|
||||
fetchOptions: {
|
||||
headers: cacheHeaders,
|
||||
throw: true,
|
||||
},
|
||||
});
|
||||
expect(initialSession?.user).toBeDefined();
|
||||
|
||||
const changePasswordResult = await cacheClient.changePassword({
|
||||
newPassword: "newSecurePassword123",
|
||||
currentPassword: testPassword,
|
||||
revokeOtherSessions: true,
|
||||
fetchOptions: {
|
||||
headers: cacheHeaders,
|
||||
},
|
||||
});
|
||||
|
||||
expect(changePasswordResult.data).toBeDefined();
|
||||
|
||||
const sessionAfterPasswordChange = await cacheClient.getSession({
|
||||
fetchOptions: {
|
||||
headers: cacheHeaders,
|
||||
},
|
||||
});
|
||||
|
||||
expect(sessionAfterPasswordChange.data).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,11 @@ import * as z from "zod/v4";
|
||||
import { createAuthEndpoint } from "../call";
|
||||
|
||||
import { deleteSessionCookie, setSessionCookie } from "../../cookies";
|
||||
import { getSessionFromCtx, sessionMiddleware } from "./session";
|
||||
import {
|
||||
getSessionFromCtx,
|
||||
sensitiveSessionMiddleware,
|
||||
sessionMiddleware,
|
||||
} from "./session";
|
||||
import { APIError } from "better-call";
|
||||
import { createEmailVerificationToken } from "./email-verification";
|
||||
import type { AdditionalUserFieldsInput, BetterAuthOptions } from "../../types";
|
||||
@@ -150,7 +154,7 @@ export const changePassword = createAuthEndpoint(
|
||||
})
|
||||
.optional(),
|
||||
}),
|
||||
use: [sessionMiddleware],
|
||||
use: [sensitiveSessionMiddleware],
|
||||
metadata: {
|
||||
openapi: {
|
||||
description: "Change the password of the user",
|
||||
@@ -318,7 +322,7 @@ export const setPassword = createAuthEndpoint(
|
||||
metadata: {
|
||||
SERVER_ONLY: true,
|
||||
},
|
||||
use: [sessionMiddleware],
|
||||
use: [sensitiveSessionMiddleware],
|
||||
},
|
||||
async (ctx) => {
|
||||
const { newPassword } = ctx.body;
|
||||
@@ -371,7 +375,7 @@ export const deleteUser = createAuthEndpoint(
|
||||
"/delete-user",
|
||||
{
|
||||
method: "POST",
|
||||
use: [sessionMiddleware],
|
||||
use: [sensitiveSessionMiddleware],
|
||||
body: z.object({
|
||||
/**
|
||||
* The callback URL to redirect to after the user is deleted
|
||||
@@ -662,7 +666,7 @@ export const changeEmail = createAuthEndpoint(
|
||||
})
|
||||
.optional(),
|
||||
}),
|
||||
use: [sessionMiddleware],
|
||||
use: [sensitiveSessionMiddleware],
|
||||
metadata: {
|
||||
openapi: {
|
||||
responses: {
|
||||
|
||||
Reference in New Issue
Block a user