From 43ce3a7c530186b2b9c1a83dcd4ea1e3e40c3ec0 Mon Sep 17 00:00:00 2001
From: Bryan Frimin
Date: Mon, 20 Jul 2026 09:59:25 +0200
Subject: [PATCH] Harden compliance portal auth and TLS
Align console references and OAuth branding with the
compliance-page model, and fix certificate cache eviction,
portal OAuth handlers, and magic-link edge cases left after
the trust-center rename.
Signed-off-by: Bryan Frimin
---
.../CompliancePageReferenceDialog.tsx | 12 +-
.../DeleteCompliancePageReferenceDialog.tsx | 8 +-
.../hooks/graph/TrustCenterReferenceGraph.ts | 135 -----------------
.../iam/auth/MagicLinkAlreadyUsedPage.tsx | 33 ++--
.../pages/iam/auth/MagicLinkExpiredPage.tsx | 33 ++--
.../src/pages/iam/auth/sign-in/SignInPage.tsx | 6 +-
.../sign-in/_components/MagicLinkForm.tsx | 48 ++++--
.../_lib/compliancePageReferenceMutations.ts | 141 ++++++++++++++++++
.../CompliancePageReferenceList.tsx | 4 +-
apps/trust/src/queries/TrustGraph.ts | 2 -
e2e/internal/testutil/graphql.go | 5 +-
e2e/internal/testutil/testutil.go | 2 +-
pkg/certmanager/cache_store.go | 36 ++---
pkg/certmanager/renew_worker.go | 6 +
pkg/certmanager/selector.go | 45 +++---
pkg/cmd/trust-center/update/update.go | 6 +-
pkg/complianceportal/management/domain.go | 2 +-
.../management/oauth2_scopes.go | 43 ++++--
pkg/complianceportal/management/policies.go | 1 +
pkg/complianceportal/visitor/brand.go | 22 +--
pkg/complianceportal/visitor/cimd.go | 32 ++--
...pliance_portal_commitment_group_service.go | 25 ----
pkg/complianceportal/visitor/service.go | 33 ++++
pkg/coredata/cached_certificate.go | 23 +--
pkg/coredata/custom_domain.go | 29 ++++
pkg/coredata/migrations/20260706T132923Z.sql | 2 +
pkg/coredata/migrations/20260709T090905Z.sql | 8 +-
pkg/coredata/migrations/20260710T121004Z.sql | 13 ++
pkg/coredata/migrations/20260717T121103Z.sql | 2 +-
pkg/crypto/jose/jose.go | 21 ++-
pkg/filemanager/s3_test.go | 3 +-
pkg/filemanager/serve_public.go | 10 +-
pkg/filemanager/service.go | 4 +
pkg/filemanager/url_test.go | 8 +-
pkg/iam/auth_service.go | 25 ++--
pkg/iam/oauth2/id_token.go | 20 ---
pkg/iam/organization_service.go | 42 +++---
pkg/iam/service.go | 6 +-
pkg/probod/probod.go | 2 +-
.../api/complianceportal/v1/auth_resolvers.go | 70 ++++++---
pkg/server/api/complianceportal/v1/mux.go | 2 +-
.../v1/oauth_callback_handler.go | 12 +-
.../v1/oauth_client_metadata_handler.go | 22 ++-
.../v1/oauth_initiate_handler.go | 15 +-
.../api/connect/v1/graphql/base.graphql | 2 +-
pkg/server/api/connect/v1/graphql_handler.go | 3 -
.../api/connect/v1/oauth_client_branding.go | 4 +-
pkg/server/api/connect/v1/resolver.go | 3 +-
pkg/server/response_headers.go | 26 ++--
pkg/slug/slug.go | 15 ++
pkg/slug/slug_test.go | 12 ++
51 files changed, 626 insertions(+), 458 deletions(-)
delete mode 100644 apps/console/src/hooks/graph/TrustCenterReferenceGraph.ts
create mode 100644 apps/console/src/pages/organizations/compliance-page/_lib/compliancePageReferenceMutations.ts
diff --git a/apps/console/src/components/compliancePage/CompliancePageReferenceDialog.tsx b/apps/console/src/components/compliancePage/CompliancePageReferenceDialog.tsx
index 73044fe67..d814f68b8 100644
--- a/apps/console/src/components/compliancePage/CompliancePageReferenceDialog.tsx
+++ b/apps/console/src/components/compliancePage/CompliancePageReferenceDialog.tsx
@@ -35,11 +35,11 @@ import { forwardRef, type ReactNode, useImperativeHandle, useState } from "react
import { z } from "zod";
import type { CompliancePageReferenceListItemFragment$data } from "#/__generated__/core/CompliancePageReferenceListItemFragment.graphql";
-import {
- useCreateTrustCenterReferenceMutation,
- useUpdateTrustCenterReferenceMutation,
-} from "#/hooks/graph/TrustCenterReferenceGraph";
import { useFormWithSchema } from "#/hooks/useFormWithSchema";
+import {
+ useCreateCompliancePageReferenceMutation,
+ useUpdateCompliancePageReferenceMutation,
+} from "#/pages/organizations/compliance-page/_lib/compliancePageReferenceMutations";
const referenceSchema = z.object({
name: z.string().min(1, "Name is required"),
@@ -65,8 +65,8 @@ export const CompliancePageReferenceDialog = forwardRef(null);
const [uploadedFile, setUploadedFile] = useState(null);
- const [createReference, isCreating] = useCreateTrustCenterReferenceMutation();
- const [updateReference, isUpdating] = useUpdateTrustCenterReferenceMutation();
+ const [createReference, isCreating] = useCreateCompliancePageReferenceMutation();
+ const [updateReference, isUpdating] = useUpdateCompliancePageReferenceMutation();
const { register, handleSubmit, formState: { errors }, reset } = useFormWithSchema(
referenceSchema,
diff --git a/apps/console/src/components/compliancePage/DeleteCompliancePageReferenceDialog.tsx b/apps/console/src/components/compliancePage/DeleteCompliancePageReferenceDialog.tsx
index f9545460e..a929246c1 100644
--- a/apps/console/src/components/compliancePage/DeleteCompliancePageReferenceDialog.tsx
+++ b/apps/console/src/components/compliancePage/DeleteCompliancePageReferenceDialog.tsx
@@ -30,9 +30,9 @@ import {
useDialogRef,
} from "@probo/ui";
-import type { TrustCenterReferenceGraphDeleteMutation } from "#/__generated__/core/TrustCenterReferenceGraphDeleteMutation.graphql";
-import { deleteTrustCenterReferenceMutation } from "#/hooks/graph/TrustCenterReferenceGraph";
+import type { compliancePageReferenceMutationsDeleteMutation } from "#/__generated__/core/compliancePageReferenceMutationsDeleteMutation.graphql";
import { useMutation } from "#/lib/relay/useMutation";
+import { deleteCompliancePageReferenceMutation } from "#/pages/organizations/compliance-page/_lib/compliancePageReferenceMutations";
type Props = {
children: React.ReactNode;
@@ -52,8 +52,8 @@ export function DeleteCompliancePageReferenceDialog({
const { __ } = useTranslate();
const ref = useDialogRef();
- const [mutate, isDeleting] = useMutation(
- deleteTrustCenterReferenceMutation,
+ const [mutate, isDeleting] = useMutation(
+ deleteCompliancePageReferenceMutation,
{
successMessage: __("Reference deleted successfully"),
errorToast: __("Failed to delete reference"),
diff --git a/apps/console/src/hooks/graph/TrustCenterReferenceGraph.ts b/apps/console/src/hooks/graph/TrustCenterReferenceGraph.ts
deleted file mode 100644
index 518d12ce4..000000000
--- a/apps/console/src/hooks/graph/TrustCenterReferenceGraph.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (c) 2025-2026 Probo Inc .
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-import { graphql } from "react-relay";
-
-import type { TrustCenterReferenceGraphCreateMutation } from "#/__generated__/core/TrustCenterReferenceGraphCreateMutation.graphql";
-import type { TrustCenterReferenceGraphDeleteMutation } from "#/__generated__/core/TrustCenterReferenceGraphDeleteMutation.graphql";
-import type { TrustCenterReferenceGraphUpdateMutation } from "#/__generated__/core/TrustCenterReferenceGraphUpdateMutation.graphql";
-import type { TrustCenterReferenceGraphUpdateRankMutation } from "#/__generated__/core/TrustCenterReferenceGraphUpdateRankMutation.graphql";
-import { useMutation } from "#/lib/relay/useMutation";
-
-export const createTrustCenterReferenceMutation = graphql`
- mutation TrustCenterReferenceGraphCreateMutation(
- $input: CreateTrustCenterReferenceInput!
- $connections: [ID!]!
- ) {
- createTrustCenterReference(input: $input) {
- trustCenterReferenceEdge @appendEdge(connections: $connections) {
- cursor
- node {
- id
- name
- description
- websiteUrl
- logo {
- downloadUrl
- }
- rank
- createdAt
- updatedAt
- canUpdate: permission(action: "compliance-portal:portal-reference:update")
- canDelete: permission(action: "compliance-portal:portal-reference:delete")
- }
- }
- }
- }
-`;
-
-export const updateTrustCenterReferenceMutation = graphql`
- mutation TrustCenterReferenceGraphUpdateMutation(
- $input: UpdateTrustCenterReferenceInput!
- ) {
- updateTrustCenterReference(input: $input) {
- trustCenterReference {
- id
- name
- description
- websiteUrl
- logo {
- downloadUrl
- }
- rank
- createdAt
- updatedAt
- canUpdate: permission(action: "compliance-portal:portal-reference:update")
- canDelete: permission(action: "compliance-portal:portal-reference:delete")
- }
- }
- }
-`;
-
-export const deleteTrustCenterReferenceMutation = graphql`
- mutation TrustCenterReferenceGraphDeleteMutation(
- $input: DeleteTrustCenterReferenceInput!
- $connections: [ID!]!
- ) {
- deleteTrustCenterReference(input: $input) {
- deletedTrustCenterReferenceId @deleteEdge(connections: $connections)
- }
- }
-`;
-
-export function useCreateTrustCenterReferenceMutation() {
- return useMutation(
- createTrustCenterReferenceMutation,
- {
- successMessage: "Reference created successfully",
- errorToast: "Failed to create reference",
- },
- );
-}
-
-export function useUpdateTrustCenterReferenceMutation() {
- return useMutation(
- updateTrustCenterReferenceMutation,
- {
- successMessage: "Reference updated successfully",
- errorToast: "Failed to update reference",
- },
- );
-}
-
-export const updateTrustCenterReferenceRankMutation = graphql`
- mutation TrustCenterReferenceGraphUpdateRankMutation(
- $input: UpdateTrustCenterReferenceInput!
- ) {
- updateTrustCenterReference(input: $input) {
- trustCenterReference {
- id
- rank
- }
- }
- }
-`;
-
-export function useUpdateTrustCenterReferenceRankMutation() {
- return useMutation(
- updateTrustCenterReferenceRankMutation,
- {
- successMessage: "Order updated successfully",
- errorToast: "Failed to update order",
- },
- );
-}
-
-export function useDeleteTrustCenterReferenceMutation() {
- return useMutation(
- deleteTrustCenterReferenceMutation,
- {
- successMessage: "Reference deleted successfully",
- errorToast: "Failed to delete reference",
- },
- );
-}
diff --git a/apps/console/src/pages/iam/auth/MagicLinkAlreadyUsedPage.tsx b/apps/console/src/pages/iam/auth/MagicLinkAlreadyUsedPage.tsx
index 9dd57c8e5..10f81b405 100644
--- a/apps/console/src/pages/iam/auth/MagicLinkAlreadyUsedPage.tsx
+++ b/apps/console/src/pages/iam/auth/MagicLinkAlreadyUsedPage.tsx
@@ -1,25 +1,29 @@
// Copyright (c) 2026 Probo Inc .
//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
import { usePageTitle } from "@probo/hooks";
import { useTranslate } from "@probo/i18n";
import { Button } from "@probo/ui";
-import { useNavigate } from "react-router";
export default function MagicLinkAlreadyUsedPage() {
const { __ } = useTranslate();
- const navigate = useNavigate();
usePageTitle(__("Link Already Used"));
@@ -33,10 +37,7 @@ export default function MagicLinkAlreadyUsedPage() {
)}
-