From 80e07a8d3587936f89519aeeeed67e9eeb4a3cd9 Mon Sep 17 00:00:00 2001 From: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Date: Thu, 5 Feb 2026 11:39:29 +0100 Subject: [PATCH] fix: add oauth client owner email to admin view (#27243) * add owner email to admin oauth view * fix type error * fix oauth flow with sign up * improve e2e tests * fix: use dynamic user email in OAuth client e2e test The test 'editing redirect uri of an approved client triggers reapproval' creates a new user and assigns the OAuth client to that user, but was incorrectly expecting 'admin@example.com' as the owner email. Changed to use user.email to correctly validate the owner email matches the created user. Addresses Cubic AI review feedback (confidence: 9/10) Co-Authored-By: unknown <> --------- Co-authored-by: CarinaWolli Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../settings/oauth/OAuthClientsList.tsx | 3 + .../oauth/view/OAuthClientDetailsDialog.tsx | 131 +++++++++++++----- .../oauth/oauth-client-owner-crud.e2e.ts | 74 +++++++--- 3 files changed, 156 insertions(+), 52 deletions(-) diff --git a/apps/web/modules/settings/oauth/OAuthClientsList.tsx b/apps/web/modules/settings/oauth/OAuthClientsList.tsx index 932266c0ad..403dd6b35a 100644 --- a/apps/web/modules/settings/oauth/OAuthClientsList.tsx +++ b/apps/web/modules/settings/oauth/OAuthClientsList.tsx @@ -60,6 +60,9 @@ export const OAuthClientsList = ({ />
{client.name}
+ {client.user?.email && ( +
{client.user.email}
+ )}
diff --git a/apps/web/modules/settings/oauth/view/OAuthClientDetailsDialog.tsx b/apps/web/modules/settings/oauth/view/OAuthClientDetailsDialog.tsx index efd25f6ce0..8da0bf1cf5 100644 --- a/apps/web/modules/settings/oauth/view/OAuthClientDetailsDialog.tsx +++ b/apps/web/modules/settings/oauth/view/OAuthClientDetailsDialog.tsx @@ -10,7 +10,13 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Alert } from "@calcom/ui/components/alert"; import { Badge } from "@calcom/ui/components/badge"; import { Button } from "@calcom/ui/components/button"; -import { ConfirmationDialogContent, DialogClose, DialogContent, DialogFooter } from "@calcom/ui/components/dialog"; +import { + ConfirmationDialogContent, + DialogClose, + DialogContent, + DialogFooter, +} from "@calcom/ui/components/dialog"; + import { showToast } from "@calcom/ui/components/toast"; import { Tooltip } from "@calcom/ui/components/tooltip"; import { Label, TextArea } from "@calcom/ui/components/form"; @@ -30,6 +36,9 @@ type OAuthClientDetails = { clientSecret?: string; isPkceEnabled?: boolean; clientType?: string; + user?: { + email: string; + } | null; }; const OAuthClientDetailsDialog = ({ @@ -69,7 +78,8 @@ const OAuthClientDetailsDialog = ({ const [isDeleteConfirmOpen, setIsDeleteConfirmOpen] = useState(false); const [isRejectConfirmOpen, setIsRejectConfirmOpen] = useState(false); const [rejectionReason, setRejectionReason] = useState(""); - const [showRejectionReasonError, setShowRejectionReasonError] = useState(false); + const [showRejectionReasonError, setShowRejectionReasonError] = + useState(false); const form = useForm({ defaultValues: { name: "", @@ -93,7 +103,10 @@ const OAuthClientDetailsDialog = ({ if (!client) return; const enablePkce = - client.isPkceEnabled ?? (client.clientType ? client.clientType.toUpperCase() === "PUBLIC" : false); + client.isPkceEnabled ?? + (client.clientType + ? client.clientType.toUpperCase() === "PUBLIC" + : false); const nextLogo = client.logo ?? ""; setLogo(nextLogo); @@ -139,8 +152,10 @@ const OAuthClientDetailsDialog = ({ ); if (showAdminActions) { - const canReject = Boolean(onReject) && (status === "PENDING" || status === "APPROVED"); - const canApprove = Boolean(onApprove) && (status === "PENDING" || status === "REJECTED"); + const canReject = + Boolean(onReject) && (status === "PENDING" || status === "APPROVED"); + const canApprove = + Boolean(onApprove) && (status === "PENDING" || status === "REJECTED"); return (
@@ -156,7 +171,8 @@ const OAuthClientDetailsDialog = ({ setIsRejectConfirmOpen(true); setRejectionReason(""); setShowRejectionReasonError(false); - }}> + }} + > {t("reject")} ) : null} @@ -170,7 +186,8 @@ const OAuthClientDetailsDialog = ({ onClick={() => { if (!clientId) return; onApprove?.(clientId); - }}> + }} + > {t("approve")} ) : null} @@ -182,14 +199,20 @@ const OAuthClientDetailsDialog = ({ return (
{closeButton} -
); } - return
{closeButton}
; + return ( +
{closeButton}
+ ); })(); return ( @@ -209,26 +232,42 @@ const OAuthClientDetailsDialog = ({ websiteUrl: values.websiteUrl.trim() || "", logo: values.logo, }); - })}> + })} + > {status ? (
- + {t(getStatusBadgeVariant(status).labelKey)}
) : null} {status === "PENDING" ? ( - + ) : null} {status === "APPROVED" ? ( - + ) : null} {status === "REJECTED" && client.rejectionReason ? ( -
- {t("oauth_client_rejection_reason")}: {client.rejectionReason} +
+ + {t("oauth_client_rejection_reason")}: + {" "} + {client.rejectionReason}
) : null} @@ -237,27 +276,39 @@ const OAuthClientDetailsDialog = ({
+ className="px-2 py-1 w-full font-mono text-sm truncate align-middle rounded-md rounded-r-none bg-subtle text-default" + > {client.clientId}
+ {client.user?.email ? ( +
+
{t("owner")}
+
+ {client.user.email} +
+
+ ) : null} + setIsDeleteConfirmOpen(true)}> + onClick={() => setIsDeleteConfirmOpen(true)} + > {t("delete_oauth_client")} - + { if (!client) return; onDelete?.(client.clientId); - }}> + }} + > {isDeletePending ? t("deleting") : t("delete")} - }> + } + >

{t("confirm_delete_oauth_client")}

) : null} - - {footerActions} - + {footerActions} - + + onClick={handleConfirmReject} + > {t("reject")} - }> + } + >
- +