Files
calendar/packages/trpc/server/routers/viewer/oAuth/addClient.handler.ts
T
Carina WollendorferGitHubcubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>CarinaWollicubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
4e0798577a feat: OAuth PKCE (#25313)
* add public client

* implement PKCE

* pass codeChallenge and codeChallengeMethod to handler

* fixes for secure oauth flow

* fix type error

* clean up refresh token endpoint

* only support S256

* fix type error

* remove comment

* add tests

* fix type errors in route.test.ts

* add missing support for refresh token

* add e2e test for public client refresh tokens

* allow pkce for confidential clients

* fix type error

* fix e2e

* fix option pkce for confidential clients

* e2e test improvements

* fix test

* remove only

* add delay

* fix e2e tests

* remove only

* don't skip pkce if codeChallenge is set

* add service functions for token endpoint

* use service function in refreshToken endpoint

* use repository

* remove return types

* e2e test fixes

* fix e2e test

* remove .only in e2e test

* remove pause

* fix error responses in token endpoints

* adjust tests to new error responses

* fix error responses

* e2e improvements

* redirect on error

* adjust tests

* Update apps/web/modules/auth/oauth2/authorize-view.tsx

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2025-11-26 17:02:42 +01:00

52 lines
1.4 KiB
TypeScript

import { randomBytes, createHash } from "crypto";
import { prisma } from "@calcom/prisma";
import { Prisma } from "@calcom/prisma/client";
import type { TAddClientInputSchema } from "./addClient.schema";
type AddClientOptions = {
input: TAddClientInputSchema;
};
export const addClientHandler = async ({ input }: AddClientOptions) => {
const { name, redirectUri, logo, enablePkce } = input;
const clientId = randomBytes(32).toString("hex");
const clientType = enablePkce ? "PUBLIC" : "CONFIDENTIAL";
// Only generate client secret for confidential clients
const clientData: Prisma.OAuthClientCreateInput = {
name,
redirectUri,
clientId,
clientType,
logo,
};
let secret: string | undefined;
if (!enablePkce) {
const [hashedSecret, plainSecret] = generateSecret();
clientData.clientSecret = hashedSecret;
secret = plainSecret;
}
const client = await prisma.oAuthClient.create({
data: clientData,
});
return {
clientId: client.clientId,
name: client.name,
redirectUri: client.redirectUri,
logo: client.logo,
clientType: client.clientType,
clientSecret: secret, // Only return plain secret for confidential clients
isPkceEnabled: enablePkce,
};
};
const hashSecretKey = (apiKey: string): string => createHash("sha256").update(apiKey).digest("hex");
// Generate a random secret
export const generateSecret = (secret = randomBytes(32).toString("hex")) => [hashSecretKey(secret), secret];