mirror of
https://github.com/better-auth/better-auth.git
synced 2026-07-20 00:33:45 -05:00
fix(auth): mark plugin-owned session fields as non-input (#9965)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"better-auth": patch
|
||||
---
|
||||
|
||||
Passing `activeOrganizationId`, `activeTeamId`, or `impersonatedBy` to `/update-session` now returns a 400. Change these plugin-managed session fields through their dedicated endpoints instead, such as `organization.setActive`.
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
} from "vitest";
|
||||
import { parseCookies, parseSetCookieHeader } from "../../cookies";
|
||||
import { signJWT, verifyJWT } from "../../crypto";
|
||||
import { admin } from "../../plugins/admin";
|
||||
import { organization } from "../../plugins/organization";
|
||||
import { getTestInstance } from "../../test-utils/test-instance";
|
||||
import { getDate } from "../../utils/date";
|
||||
import { freshSessionMiddleware, getSessionFromCtx } from "./session";
|
||||
@@ -2208,3 +2210,27 @@ describe("updateSession", async () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("updateSession plugin authority fields", async () => {
|
||||
// Plugin-owned authority fields must not be writable through the generic
|
||||
// session update route. They are set only by membership/permission-checked
|
||||
// setters (setActiveOrganization, impersonateUser), so /update-session must
|
||||
// reject them even though they live on the session schema.
|
||||
const { client, signInWithTestUser } = await getTestInstance({
|
||||
plugins: [organization({ teams: { enabled: true } }), admin()],
|
||||
});
|
||||
|
||||
it.each([
|
||||
"activeOrganizationId",
|
||||
"activeTeamId",
|
||||
"impersonatedBy",
|
||||
])("should reject forging the %s session field", async (field) => {
|
||||
const { runWithUser } = await signInWithTestUser();
|
||||
await runWithUser(async () => {
|
||||
const res = await client.updateSession({
|
||||
[field]: "forged-value",
|
||||
} as any);
|
||||
expect(res.error?.status).toBe(400);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,6 +40,11 @@ function getFields(
|
||||
...coreSchema,
|
||||
...(additionalFields ?? {}),
|
||||
};
|
||||
// FIXME: Plugin-contributed fields are input-by-default, so a plugin-owned
|
||||
// authority field is writable through generic input routes (e.g.
|
||||
// /update-session) unless it sets `input: false`. A future breaking change
|
||||
// should make plugin fields non-input by default and require an explicit
|
||||
// opt-in for client-writable ones.
|
||||
for (const plugin of options.plugins || []) {
|
||||
if (plugin.schema && plugin.schema[modelName]) {
|
||||
schema = {
|
||||
|
||||
@@ -31,6 +31,7 @@ export const schema = {
|
||||
impersonatedBy: {
|
||||
type: "string",
|
||||
required: false,
|
||||
input: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -166,11 +166,13 @@ describe("organization", async () => {
|
||||
activeOrganizationId: {
|
||||
type: "string";
|
||||
required: false;
|
||||
input: false;
|
||||
};
|
||||
} & {
|
||||
activeTeamId: {
|
||||
type: "string";
|
||||
required: false;
|
||||
input: false;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1221,6 +1221,7 @@ export function organization<O extends OrganizationOptions>(options?: O) {
|
||||
activeOrganizationId: {
|
||||
type: "string",
|
||||
required: false,
|
||||
input: false,
|
||||
fieldName: opts.schema?.session?.fields?.activeOrganizationId,
|
||||
},
|
||||
...(teamSupport
|
||||
@@ -1228,6 +1229,7 @@ export function organization<O extends OrganizationOptions>(options?: O) {
|
||||
activeTeamId: {
|
||||
type: "string",
|
||||
required: false,
|
||||
input: false,
|
||||
fieldName: opts.schema?.session?.fields?.activeTeamId,
|
||||
},
|
||||
}
|
||||
@@ -1239,16 +1241,19 @@ export function organization<O extends OrganizationOptions>(options?: O) {
|
||||
activeTeamId: {
|
||||
type: "string";
|
||||
required: false;
|
||||
input: false;
|
||||
};
|
||||
activeOrganizationId: {
|
||||
type: "string";
|
||||
required: false;
|
||||
input: false;
|
||||
};
|
||||
}
|
||||
: {
|
||||
activeOrganizationId: {
|
||||
type: "string";
|
||||
required: false;
|
||||
input: false;
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
@@ -199,6 +199,7 @@ interface SessionDefaultFields {
|
||||
activeOrganizationId: {
|
||||
type: "string";
|
||||
required: false;
|
||||
input: false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -278,6 +279,7 @@ export type OrganizationSchema<O extends OrganizationOptions> =
|
||||
activeTeamId: {
|
||||
type: "string";
|
||||
required: false;
|
||||
input: false;
|
||||
};
|
||||
}
|
||||
: {});
|
||||
|
||||
Reference in New Issue
Block a user