patch: 🩹 attach has metadata stripped out
This commit is contained in:
@@ -13,11 +13,13 @@ import { resolveIdentity } from "./resolveIdentity";
|
||||
const buildSdkArgs = ({
|
||||
body,
|
||||
identity,
|
||||
route,
|
||||
}: {
|
||||
body: unknown;
|
||||
identity: ResolvedIdentity;
|
||||
route: RouteDefinition;
|
||||
}): Record<string, unknown> => {
|
||||
const args = sanitizeBody(body);
|
||||
const args = sanitizeBody(body, route.protectedBodyFields);
|
||||
|
||||
if (identity.customerId) {
|
||||
args.customerId = identity.customerId;
|
||||
@@ -71,7 +73,7 @@ export const executeRoute = async ({
|
||||
}
|
||||
|
||||
// 3. Build args and call SDK
|
||||
const sdkArgs = buildSdkArgs({ body, identity });
|
||||
const sdkArgs = buildSdkArgs({ body, identity, route });
|
||||
|
||||
try {
|
||||
const result = await route.sdkMethod(autumn, sdkArgs);
|
||||
|
||||
@@ -16,7 +16,12 @@ import {
|
||||
updateSubscriptionParamsSchema,
|
||||
} from "../../../generated";
|
||||
import type { RouteDefinition, RouteName } from "../types";
|
||||
import { backendError, backendSuccess, sanitizeBody } from "../utils";
|
||||
import {
|
||||
backendError,
|
||||
backendSuccess,
|
||||
CUSTOMER_PROTECTED_BODY_FIELDS,
|
||||
sanitizeBody,
|
||||
} from "../utils";
|
||||
|
||||
const getEntityBodySchema = z.object({
|
||||
entityId: z.string(),
|
||||
@@ -33,8 +38,9 @@ export const routeConfigs: RouteDefinition<RouteName>[] = [
|
||||
// expand: z.array(z.enum(CustomerExpand)).optional(),
|
||||
expand: z.array(z.string()).optional(),
|
||||
}),
|
||||
protectedBodyFields: CUSTOMER_PROTECTED_BODY_FIELDS,
|
||||
customHandler: async ({ autumn, identity, body }) => {
|
||||
const sanitizedBody = sanitizeBody(body);
|
||||
const sanitizedBody = sanitizeBody(body, CUSTOMER_PROTECTED_BODY_FIELDS);
|
||||
|
||||
// Special case: if no customer and errorOnNotFound is false, return 204
|
||||
if (!identity?.customerId && sanitizedBody.errorOnNotFound === false) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Autumn } from "@useautumn/sdk";
|
||||
import type { z } from "zod/v4";
|
||||
import type { ProtectedBodyField } from "../utils/sanitizeBody";
|
||||
import type { ResolvedIdentity } from "./authTypes";
|
||||
import type { BackendResult } from "./responseTypes";
|
||||
|
||||
@@ -48,6 +49,8 @@ export type RouteDefinition<T extends RouteName = RouteName> = {
|
||||
customHandler?: CustomHandlerFn;
|
||||
/** Whether customer ID is required (default: true) */
|
||||
requireCustomer?: boolean;
|
||||
/** Body fields that must come from identity, not frontend */
|
||||
protectedBodyFields?: readonly ProtectedBodyField[];
|
||||
/** Zod schema for request body validation (used by better-auth plugin) */
|
||||
bodySchema?: z.ZodTypeAny;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
export { secretKeyCheck } from "./secretKeyCheck";
|
||||
export { backendSuccess, backendError, isBackendResult } from "./backendRes";
|
||||
export { sanitizeBody } from "./sanitizeBody";
|
||||
export {
|
||||
CUSTOMER_PROTECTED_BODY_FIELDS,
|
||||
DEFAULT_PROTECTED_BODY_FIELDS,
|
||||
sanitizeBody,
|
||||
} from "./sanitizeBody";
|
||||
export type { ProtectedBodyField } from "./sanitizeBody";
|
||||
|
||||
@@ -1,19 +1,31 @@
|
||||
/** Fields that must come from identity, not frontend */
|
||||
const PROTECTED_FIELDS = [
|
||||
export const DEFAULT_PROTECTED_BODY_FIELDS = [
|
||||
"customerId",
|
||||
"customerData",
|
||||
"name",
|
||||
"email",
|
||||
"metadata",
|
||||
"stripeId",
|
||||
];
|
||||
] as const;
|
||||
|
||||
export const CUSTOMER_PROTECTED_BODY_FIELDS = [
|
||||
...DEFAULT_PROTECTED_BODY_FIELDS,
|
||||
"metadata",
|
||||
] as const;
|
||||
|
||||
export type ProtectedBodyField =
|
||||
| (typeof DEFAULT_PROTECTED_BODY_FIELDS)[number]
|
||||
| (typeof CUSTOMER_PROTECTED_BODY_FIELDS)[number];
|
||||
|
||||
/** Strip protected fields from body to prevent spoofing */
|
||||
export const sanitizeBody = (body: unknown): Record<string, unknown> => {
|
||||
export const sanitizeBody = (
|
||||
body: unknown,
|
||||
protectedFields: readonly ProtectedBodyField[] = DEFAULT_PROTECTED_BODY_FIELDS,
|
||||
): Record<string, unknown> => {
|
||||
const rawBody = (body as Record<string, unknown>) || {};
|
||||
const sanitized: Record<string, unknown> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(rawBody)) {
|
||||
if (!PROTECTED_FIELDS.includes(key)) {
|
||||
if (!protectedFields.includes(key as ProtectedBodyField)) {
|
||||
sanitized[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user