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