fix: req.formData() should be used in some API routes (#19930)

This commit is contained in:
Benny Joo
2025-03-11 07:51:33 +00:00
committed by GitHub
parent 2f9a853023
commit 517d509321
14 changed files with 59 additions and 14 deletions
@@ -1,4 +1,5 @@
import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir";
import { parseRequestData } from "app/api/parseRequestData";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
@@ -8,7 +9,8 @@ import { emailSchema } from "@calcom/lib/emailSchema";
import prisma from "@calcom/prisma";
async function handler(req: NextRequest) {
const email = emailSchema.transform((val) => val.toLowerCase()).safeParse((await req.json())?.email);
const body = await parseRequestData(req);
const email = emailSchema.transform((val) => val.toLowerCase()).safeParse(body?.email);
if (!email.success) {
return NextResponse.json({ message: "email is required" }, { status: 400 });
@@ -1,3 +1,4 @@
import { parseRequestData } from "app/api/parseRequestData";
import jwt from "jsonwebtoken";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
@@ -7,7 +8,11 @@ import { generateSecret } from "@calcom/trpc/server/routers/viewer/oAuth/addClie
import type { OAuthTokenPayload } from "@calcom/types/oauth";
export async function POST(req: NextRequest) {
const { client_id, client_secret, grant_type } = await req.json();
const { client_id, client_secret, grant_type } = await parseRequestData(req);
if (!client_id || !client_secret) {
return NextResponse.json({ message: "Missing client id or secret" }, { status: 400 });
}
if (grant_type !== "refresh_token") {
return NextResponse.json({ message: "grant type invalid" }, { status: 400 });
+2 -1
View File
@@ -1,3 +1,4 @@
import { parseRequestData } from "app/api/parseRequestData";
import jwt from "jsonwebtoken";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
@@ -7,7 +8,7 @@ import { generateSecret } from "@calcom/trpc/server/routers/viewer/oAuth/addClie
import type { OAuthTokenPayload } from "@calcom/types/oauth";
export async function POST(req: NextRequest) {
const { code, client_id, client_secret, grant_type, redirect_uri } = await req.json();
const { code, client_id, client_secret, grant_type, redirect_uri } = await parseRequestData(req);
if (grant_type !== "authorization_code") {
return NextResponse.json({ message: "grant_type invalid" }, { status: 400 });
@@ -1,3 +1,4 @@
import { parseRequestData } from "app/api/parseRequestData";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { z } from "zod";
@@ -15,9 +16,9 @@ const passwordResetRequestSchema = z.object({
});
async function handler(req: NextRequest) {
const { password: rawPassword, requestId: rawRequestId } = passwordResetRequestSchema.parse(
await req.json()
);
const body = await parseRequestData(req);
const { password: rawPassword, requestId: rawRequestId } = passwordResetRequestSchema.parse(body);
// rate-limited there is a low, very low chance that a password request stays valid long enough
// to brute force 3.8126967e+40 options.
const maybeRequest = await prisma.resetPasswordRequest.findFirstOrThrow({
@@ -1,8 +1,8 @@
import type { OAuthReq } from "@boxyhq/saml-jackson";
import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import type { OAuthReq } from "@calcom/features/ee/sso/lib/jackson";
import jackson from "@calcom/features/ee/sso/lib/jackson";
import type { HttpError } from "@calcom/lib/http-error";
+5 -1
View File
@@ -1,13 +1,17 @@
import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir";
import { parseRequestData } from "app/api/parseRequestData";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import jackson from "@calcom/features/ee/sso/lib/jackson";
import type { SAMLResponsePayload } from "@calcom/features/ee/sso/lib/jackson";
async function handler(req: NextRequest) {
const { oauthController } = await jackson();
const { redirect_url } = await oauthController.samlResponse(await req.json());
const { redirect_url } = await oauthController.samlResponse(
(await parseRequestData(req)) as SAMLResponsePayload
);
if (redirect_url) {
return NextResponse.redirect(redirect_url, 302);
+3 -1
View File
@@ -1,12 +1,14 @@
import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir";
import { parseRequestData } from "app/api/parseRequestData";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import jackson from "@calcom/features/ee/sso/lib/jackson";
import type { OAuthTokenReq } from "@calcom/features/ee/sso/lib/jackson";
async function handler(req: NextRequest) {
const { oauthController } = await jackson();
const tokenResponse = await oauthController.token(await req.json());
const tokenResponse = await oauthController.token((await parseRequestData(req)) as OAuthTokenReq);
return NextResponse.json(tokenResponse);
}
+3 -1
View File
@@ -1,4 +1,5 @@
import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir";
import { parseRequestData } from "app/api/parseRequestData";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import z from "zod";
@@ -29,8 +30,9 @@ async function handler(req: NextRequest) {
if (userCount !== 0) {
throw new HttpError({ statusCode: 400, message: "No setup needed." });
}
const body = await parseRequestData(req);
const parsedQuery = querySchema.safeParse(await req.json());
const parsedQuery = querySchema.safeParse(body);
if (!parsedQuery.success) {
throw new HttpError({ statusCode: 422, message: parsedQuery.error.message });
}
@@ -1,3 +1,4 @@
import { parseRequestData } from "app/api/parseRequestData";
import { cookies, headers } from "next/headers";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
@@ -13,7 +14,7 @@ import { IdentityProvider } from "@calcom/prisma/client";
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
export async function POST(req: NextRequest) {
const body = await req.json();
const body = await parseRequestData(req);
const session = await getServerSession({ req: buildLegacyRequest(headers(), cookies()) });
if (!session) {
@@ -1,3 +1,4 @@
import { parseRequestData } from "app/api/parseRequestData";
import { cookies, headers } from "next/headers";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
@@ -11,7 +12,7 @@ import prisma from "@calcom/prisma";
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
export async function POST(req: NextRequest) {
const body = await req.json();
const body = await parseRequestData(req);
const session = await getServerSession({ req: buildLegacyRequest(headers(), cookies()) });
if (!session) {
@@ -1,3 +1,4 @@
import { parseRequestData } from "app/api/parseRequestData";
import crypto from "crypto";
import { cookies, headers } from "next/headers";
import { NextResponse } from "next/server";
@@ -15,7 +16,7 @@ import { IdentityProvider } from "@calcom/prisma/enums";
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
export async function POST(req: NextRequest) {
const body = await req.json();
const body = await parseRequestData(req);
const session = await getServerSession({ req: buildLegacyRequest(headers(), cookies()) });
if (!session) {
+19
View File
@@ -0,0 +1,19 @@
import type { NextRequest } from "next/server";
export async function parseRequestData(req: NextRequest): Promise<Record<string, any>> {
try {
return await req.json();
} catch (error) {
const contentType = req.headers.get("content-type") || "";
// Handle form data requests
if (
contentType.includes("application/x-www-form-urlencoded") ||
contentType.includes("multipart/form-data")
) {
const formData = await req.formData();
return Object.fromEntries(formData.entries());
}
throw new Error(`Unsupported content type: ${contentType}`);
}
}
@@ -1,3 +1,4 @@
import { parseRequestData } from "app/api/parseRequestData";
import { headers, cookies } from "next/headers";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
@@ -55,7 +56,7 @@ export async function POST(request: NextRequest) {
try {
const { action, token, bookingUid, userId } = querySchema.parse(queryParams);
const body = await request.json().catch(() => ({}));
const body = await parseRequestData(request).catch(() => ({}));
const { reason } = z.object({ reason: z.string().optional() }).parse(body || {});
return await handleBookingAction(action, token, bookingUid, userId, reason, request);
+5
View File
@@ -4,12 +4,17 @@ import type {
ISPSSOConfig,
JacksonOption,
IDirectorySyncController,
OAuthTokenReq,
OAuthReq,
SAMLResponsePayload,
} from "@boxyhq/saml-jackson";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { clientSecretVerifier, oidcPath, samlAudience, samlDatabaseUrl, samlPath } from "./saml";
export type { OAuthTokenReq, OAuthReq, SAMLResponsePayload };
// Set the required options. Refer to https://github.com/boxyhq/jackson#configuration for the full list
const opts: JacksonOption = {
externalUrl: WEBAPP_URL,