Files
calendar/apps/web/app/api/auth/oauth/refreshToken/route.ts
T
02b57eb82e fix: Add refresh token to refresh-token request response (#24831)
* Add refresh token to refresh-token request response

This change addresses an issue reported in https://github.com/calcom/cal.com/issues/23939#issuecomment-3443480099 in which a new refresh token is not included in the OAuth2 refresh-token endpoint response.

* Address failing type checks

---------

Co-authored-by: Pallav <90088723+Pallava-Joshi@users.noreply.github.com>
2025-11-03 19:10:36 +05:30

81 lines
2.5 KiB
TypeScript

import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir";
import { parseUrlFormData } from "app/api/parseRequestData";
import jwt from "jsonwebtoken";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import prisma from "@calcom/prisma";
import { generateSecret } from "@calcom/trpc/server/routers/viewer/oAuth/addClient.handler";
import type { OAuthTokenPayload } from "@calcom/types/oauth";
async function handler(req: NextRequest) {
const { client_id, client_secret, grant_type } = await parseUrlFormData(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 });
}
const [hashedSecret] = generateSecret(client_secret);
const client = await prisma.oAuthClient.findFirst({
where: {
clientId: client_id,
clientSecret: hashedSecret,
},
select: {
redirectUri: true,
},
});
if (!client) {
return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
}
const secretKey = process.env.CALENDSO_ENCRYPTION_KEY || "";
let decodedRefreshToken: OAuthTokenPayload;
try {
const refreshToken = req.headers.get("authorization")?.split(" ")[1] || "";
decodedRefreshToken = jwt.verify(refreshToken, secretKey) as OAuthTokenPayload;
} catch {
return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
}
if (!decodedRefreshToken || decodedRefreshToken.token_type !== "Refresh Token") {
return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
}
const payloadAuthToken: OAuthTokenPayload = {
userId: decodedRefreshToken.userId,
teamId: decodedRefreshToken.teamId,
scope: decodedRefreshToken.scope,
token_type: "Access Token",
clientId: client_id,
};
const payloadRefreshToken: OAuthTokenPayload = {
userId: decodedRefreshToken.userId,
teamId: decodedRefreshToken.teamId,
scope: decodedRefreshToken.scope,
token_type: "Refresh Token",
clientId: client_id,
};
const access_token = jwt.sign(payloadAuthToken, secretKey, {
expiresIn: 1800, // 30 min
});
const refresh_token = jwt.sign(payloadRefreshToken, secretKey, {
expiresIn: 30 * 24 * 60 * 60, // 30 days
});
return NextResponse.json({ access_token, refresh_token }, { status: 200 });
}
export const POST = defaultResponderForAppDir(handler);