diff --git a/backend/internal/handler/dev_handler.go b/backend/internal/handler/dev_handler.go index cce0d541..1f9ba05a 100644 --- a/backend/internal/handler/dev_handler.go +++ b/backend/internal/handler/dev_handler.go @@ -28,6 +28,7 @@ type clientSummary struct { CreatedAt *time.Time `json:"createdAt,omitempty"` RedirectURIs []string `json:"redirectUris"` Scopes []string `json:"scopes"` + ClientSecret string `json:"clientSecret,omitempty"` Metadata map[string]interface{} `json:"metadata,omitempty"` } @@ -227,7 +228,7 @@ func (h *DevHandler) CreateClient(c *fiber.Ctx) error { } } - client := service.HydraClient{ + clientReq := service.HydraClient{ ClientID: clientID, ClientName: name, RedirectURIs: redirectURIs, @@ -238,11 +239,20 @@ func (h *DevHandler) CreateClient(c *fiber.Ctx) error { Metadata: metadata, } - created, err := h.Hydra.CreateClient(c.Context(), client) + created, err := h.Hydra.CreateClient(c.Context(), clientReq) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } + // Store secret in metadata for later retrieval + if created.ClientSecret != "" { + if created.Metadata == nil { + created.Metadata = map[string]interface{}{} + } + created.Metadata["client_secret"] = created.ClientSecret + _, _ = h.Hydra.UpdateClient(c.Context(), created.ClientID, *created) + } + summary := mapClientSummary(*created) return c.Status(fiber.StatusCreated).JSON(clientDetailResponse{ Client: summary, @@ -433,6 +443,7 @@ func mapClientSummary(client service.HydraClient) clientSummary { Status: status, RedirectURIs: client.RedirectURIs, Scopes: scopes, + ClientSecret: client.ClientSecret, Metadata: client.Metadata, } } diff --git a/backend/internal/service/hydra_admin_service.go b/backend/internal/service/hydra_admin_service.go index 42bf713c..6d77cebf 100644 --- a/backend/internal/service/hydra_admin_service.go +++ b/backend/internal/service/hydra_admin_service.go @@ -27,6 +27,7 @@ type HydraAdminService struct { type HydraClient struct { ClientID string `json:"client_id"` ClientName string `json:"client_name,omitempty"` + ClientSecret string `json:"client_secret,omitempty"` // Added RedirectURIs []string `json:"redirect_uris,omitempty"` GrantTypes []string `json:"grant_types,omitempty"` ResponseTypes []string `json:"response_types,omitempty"` diff --git a/devfront/src/features/clients/ClientDetailsPage.tsx b/devfront/src/features/clients/ClientDetailsPage.tsx index 48035dd4..96baf9f8 100644 --- a/devfront/src/features/clients/ClientDetailsPage.tsx +++ b/devfront/src/features/clients/ClientDetailsPage.tsx @@ -1,6 +1,7 @@ +import React, { useState, useEffect } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import type { AxiosError } from "axios"; -import { AlertCircle, Copy, Eye, Link2, Shield, Workflow, Save } from "lucide-react"; +import { AlertCircle, Copy, Eye, EyeOff, Link2, Shield, Workflow, Save } from "lucide-react"; import { Link, useParams } from "react-router-dom"; import { Badge } from "../../components/ui/badge"; import { Button } from "../../components/ui/button"; @@ -15,7 +16,7 @@ import { import { Textarea } from "../../components/ui/textarea"; import { Label } from "../../components/ui/label"; import { fetchClient, updateClient } from "../../lib/devApi"; -import { useState, useEffect } from "react"; +import { cn } from "../../lib/utils"; function ClientDetailsPage() { const params = useParams(); @@ -29,6 +30,7 @@ function ClientDetailsPage() { }); const [redirectUris, setRedirectUris] = useState(""); + const [showSecret, setShowSecret] = useState(false); useEffect(() => { if (data?.client?.redirectUris) { @@ -80,6 +82,9 @@ function ClientDetailsPage() { { label: "UserInfo Endpoint", value: data.endpoints.userinfo }, ]; + // Client Secret from API + const clientSecret = data.client.clientSecret || "SECRET_NOT_AVAILABLE"; + return (
@@ -159,10 +164,28 @@ function ClientDetailsPage() { Client Secret

-

••••••••••••••••

+

+ {showSecret ? clientSecret : "••••••••••••••••"} +

- +