fix: ignore cookiecache on auth sensitive functions (#4530)

This commit is contained in:
KinfeMichael Tariku
2025-09-09 03:15:57 +03:00
committed by GitHub
parent 8eb64f4567
commit e2a959eefe
3 changed files with 89 additions and 8 deletions

View File

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

View File

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

View File

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