chore: migrate saml/oidc APIs to App Router (#19515)

* migrate oidc

* migrate saml apis

* remove comment

* add force-dynamic

* fix

* fix

* refactor

---------

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
This commit is contained in:
Benny Joo
2025-03-10 14:36:11 +00:00
committed by GitHub
co-authored by sean-brydon
parent 631c5f7c53
commit 8d18b3da38
10 changed files with 144 additions and 127 deletions
+41
View File
@@ -0,0 +1,41 @@
import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import jackson from "@calcom/features/ee/sso/lib/jackson";
import { HttpError } from "@calcom/lib/http-error";
// This is the callback endpoint for the OIDC provider
// A team must set this endpoint in the OIDC provider's configuration
async function handler(req: NextRequest) {
const { searchParams } = req.nextUrl;
const code = searchParams.get("code");
const state = searchParams.get("state");
if (!code || !state) {
return NextResponse.json({ message: "Code and state are required" }, { status: 400 });
}
const { oauthController } = await jackson();
try {
const { redirect_url } = await oauthController.oidcAuthzResponse({ code, state });
if (!redirect_url) {
throw new HttpError({
message: "No redirect URL found",
statusCode: 500,
});
}
return NextResponse.redirect(redirect_url, 302);
} catch (err) {
const { message, statusCode = 500 } = err as HttpError;
return NextResponse.json({ message }, { status: statusCode });
}
}
const getHandler = defaultResponderForAppDir(handler);
export { getHandler as GET };
@@ -0,0 +1,29 @@
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 jackson from "@calcom/features/ee/sso/lib/jackson";
import type { HttpError } from "@calcom/lib/http-error";
async function handler(req: NextRequest) {
const { oauthController } = await jackson();
try {
const { redirect_url } = await oauthController.authorize(
Object.fromEntries(req.nextUrl.searchParams) as unknown as OAuthReq
);
return NextResponse.redirect(redirect_url as string, 302);
} catch (err) {
const { message, statusCode = 500 } = err as HttpError;
return NextResponse.json({ message }, { status: statusCode });
}
}
const getHandler = defaultResponderForAppDir(handler);
export { getHandler as GET };
export const dynamic = "force-dynamic";
@@ -0,0 +1,21 @@
import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import jackson 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());
if (redirect_url) {
return NextResponse.redirect(redirect_url, 302);
}
return NextResponse.json({ message: "No redirect URL provided" }, { status: 400 });
}
const postHandler = defaultResponderForAppDir(handler);
export { postHandler as POST };
+15
View File
@@ -0,0 +1,15 @@
import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import jackson from "@calcom/features/ee/sso/lib/jackson";
async function handler(req: NextRequest) {
const { oauthController } = await jackson();
const tokenResponse = await oauthController.token(await req.json());
return NextResponse.json(tokenResponse);
}
const postHandler = defaultResponderForAppDir(handler);
export { postHandler as POST };
@@ -0,0 +1,38 @@
import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import z from "zod";
import jackson from "@calcom/features/ee/sso/lib/jackson";
import { HttpError } from "@calcom/lib/http-error";
const extractAuthToken = (req: NextRequest) => {
const authHeader = req.headers.get("authorization");
const parts = (authHeader || "").split(" ");
if (parts.length > 1) return parts[1];
// check for query param
let arr: string[] = [];
const { access_token } = requestQuery.parse(Object.fromEntries(req.nextUrl.searchParams));
arr = arr.concat(access_token);
if (arr[0].length > 0) return arr[0];
throw new HttpError({ statusCode: 401, message: "Unauthorized" });
};
const requestQuery = z.object({
access_token: z.string(),
});
async function handler(req: NextRequest) {
const { oauthController } = await jackson();
const token = extractAuthToken(req);
const userInfo = await oauthController.userInfo(token);
return NextResponse.json(userInfo);
}
const getHandler = defaultResponderForAppDir(handler);
export { getHandler as GET };
export const dynamic = "force-dynamic";
-36
View File
@@ -1,36 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
import jackson from "@calcom/features/ee/sso/lib/jackson";
import { HttpError } from "@calcom/lib/http-error";
// This is the callback endpoint for the OIDC provider
// A team must set this endpoint in the OIDC provider's configuration
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "GET") {
return res.status(400).send("Method not allowed");
}
const { code, state } = req.query as {
code: string;
state: string;
};
const { oauthController } = await jackson();
try {
const { redirect_url } = await oauthController.oidcAuthzResponse({ code, state });
if (!redirect_url) {
throw new HttpError({
message: "No redirect URL found",
statusCode: 500,
});
}
return res.redirect(302, redirect_url);
} catch (err) {
const { message, statusCode = 500 } = err as HttpError;
return res.status(statusCode).send(message);
}
}
-23
View File
@@ -1,23 +0,0 @@
import type { OAuthReq } from "@boxyhq/saml-jackson";
import type { NextApiRequest, NextApiResponse } from "next";
import jackson from "@calcom/features/ee/sso/lib/jackson";
import type { HttpError } from "@calcom/lib/http-error";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { oauthController } = await jackson();
if (req.method !== "GET") {
return res.status(400).send("Method not allowed");
}
try {
const { redirect_url } = await oauthController.authorize(req.query as unknown as OAuthReq);
return res.redirect(302, redirect_url as string);
} catch (err) {
const { message, statusCode = 500 } = err as HttpError;
return res.status(statusCode).send(message);
}
}
-19
View File
@@ -1,19 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
import jackson from "@calcom/features/ee/sso/lib/jackson";
import { defaultHandler } from "@calcom/lib/server/defaultHandler";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
async function postHandler(req: NextApiRequest, res: NextApiResponse) {
const { oauthController } = await jackson();
const { redirect_url } = await oauthController.samlResponse(req.body);
if (redirect_url) {
res.redirect(302, redirect_url);
}
}
export default defaultHandler({
POST: Promise.resolve({ default: defaultResponder(postHandler) }),
});
-14
View File
@@ -1,14 +0,0 @@
import type { NextApiRequest } from "next";
import jackson from "@calcom/features/ee/sso/lib/jackson";
import { defaultHandler } from "@calcom/lib/server/defaultHandler";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
async function postHandler(req: NextApiRequest) {
const { oauthController } = await jackson();
return await oauthController.token(req.body);
}
export default defaultHandler({
POST: Promise.resolve({ default: defaultResponder(postHandler) }),
});
-35
View File
@@ -1,35 +0,0 @@
import type { NextApiRequest } from "next";
import z from "zod";
import jackson from "@calcom/features/ee/sso/lib/jackson";
import { HttpError } from "@calcom/lib/http-error";
import { defaultHandler } from "@calcom/lib/server/defaultHandler";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
const extractAuthToken = (req: NextApiRequest) => {
const authHeader = req.headers["authorization"];
const parts = (authHeader || "").split(" ");
if (parts.length > 1) return parts[1];
// check for query param
let arr: string[] = [];
const { access_token } = requestQuery.parse(req.query);
arr = arr.concat(access_token);
if (arr[0].length > 0) return arr[0];
throw new HttpError({ statusCode: 401, message: "Unauthorized" });
};
const requestQuery = z.object({
access_token: z.string(),
});
async function getHandler(req: NextApiRequest) {
const { oauthController } = await jackson();
const token = extractAuthToken(req);
return await oauthController.userInfo(token);
}
export default defaultHandler({
GET: Promise.resolve({ default: defaultResponder(getHandler) }),
});