diff --git a/backend/auth/middleware.go b/backend/auth/middleware.go index 920cdf02f..2274db519 100644 --- a/backend/auth/middleware.go +++ b/backend/auth/middleware.go @@ -44,6 +44,10 @@ type ClientType string const ( ClientTypeUI ClientType = "ui" ClientTypeCLI ClientType = "cli" + + // UIIdentity is the well-known identity value that the UI sends in bind requests. + // The backend resolves it to the actual identity derived from the authenticated session. + UIIdentity = "ui-identity" ) type AuthContext struct { diff --git a/backend/http/handler.go b/backend/http/handler.go index 1cf877fee..990755036 100644 --- a/backend/http/handler.go +++ b/backend/http/handler.go @@ -360,14 +360,26 @@ func (h *handler) handleBind(w http.ResponseWriter, r *http.Request) { return } - // TODO: Move to validating admission. - if bindRequest.Spec.ClusterIdentity.Identity == "" { - logger.Error(fmt.Errorf("missing cluster identity"), "invalid bind request") - writeErrorResponse(w, http.StatusBadRequest, kubebindv1alpha2.ErrorCodeBadRequest, "Missing cluster identity in bind request", "The cluster identity must be provided in the bind request") + // Identity is always required. CLI provides the cluster identity (kube-system UID), + // and the UI sends the well-known "ui-identity" value. + identity := bindRequest.Spec.ClusterIdentity.Identity + if identity == "" { + writeErrorResponse(w, http.StatusBadRequest, kubebindv1alpha2.ErrorCodeBadRequest, "Missing cluster identity", "spec.clusterIdentity.identity is required") return } - handleResult, err := h.kubeManager.HandleResources(r.Context(), state.Token.Subject, params.ConsumerID, params.ClusterID) + // Resolve the UI sentinel to a real identity derived from the authenticated session. + if identity == auth.UIIdentity { + identity = state.Token.Issuer + "/" + state.Token.Subject + logger.Info("Resolved ui-identity from session", "identity", identity) + } + + consumerID := params.ConsumerID + if consumerID == "" { + consumerID = identity + } + + handleResult, err := h.kubeManager.HandleResources(r.Context(), state.Token.Subject, consumerID, params.ClusterID) if err != nil { logger.Error(err, "failed to handle resources") statusCode, code, details := mapErrorToCode(err) diff --git a/test/e2e/bind/happy-case_test.go b/test/e2e/bind/happy-case_test.go index fcb87739d..ceb84d274 100644 --- a/test/e2e/bind/happy-case_test.go +++ b/test/e2e/bind/happy-case_test.go @@ -227,10 +227,11 @@ func testHappyCase( } t.Logf("Creating consumer workspace and starting konnector") - consumer1Config, consumer1Kubeconfig := framework.NewWorkspace(t, framework.ClientConfig(t), framework.WithName("%s-consumer-%s", name, suffix)) + consumer1Config, consumer1Kubeconfig := framework.NewWorkspace(t, framework.ClientConfig(t), + framework.WithGenerateName("%s-consumer-%s-", name, suffix)) + consumer2Config, consumer2Kubeconfig := framework.NewWorkspace(t, framework.ClientConfig(t), + framework.WithGenerateName("%s-consumer-%s-", name, suffix)) framework.StartKonnector(t, consumer1Config, "--kubeconfig="+consumer1Kubeconfig, "--server-address=:0") - - consumer2Config, consumer2Kubeconfig := framework.NewWorkspace(t, framework.ClientConfig(t), framework.WithName("%s-consumer-%s", name, suffix)) framework.StartKonnector(t, consumer2Config, "--kubeconfig="+consumer2Kubeconfig, "--server-address=:0") serviceGVR := schema.GroupVersionResource{Group: "wildwest.dev", Version: "v1alpha1", Resource: "cowboys"} diff --git a/web/src/views/Resources.vue b/web/src/views/Resources.vue index 048902859..5e9988127 100644 --- a/web/src/views/Resources.vue +++ b/web/src/views/Resources.vue @@ -321,15 +321,10 @@ const handleBind = async (templateName: string, bindingName: string) => { const bindUrl = buildApiUrl('/bind') // Create the binding request - // Use consumerId if available (CLI flow), otherwise use sessionId as cluster identity - // Read from Vue Router's route.query instead of window.location - const sessionIdFromRoute = route.query.session_id as string || '' - const clusterIdentity = consumerId.value || sessionIdFromRoute - - if (!clusterIdentity) { - showAlertModal('Missing cluster identity. Please ensure you have authenticated properly.', 'Binding Failed', 'error') - return - } + // CLI flow: use consumerId (kube-system namespace UID) + // UI flow: use the well-known "ui-identity" sentinel - the backend resolves + // it to the actual identity from the authenticated OIDC session. + const clusterIdentity = consumerId.value || 'ui-identity' const bindingRequest: BindableResourcesRequest = { metadata: {