From 3ffc345c2c8a1c02566169bc644ee95874cc0e9a Mon Sep 17 00:00:00 2001 From: kyy Date: Fri, 27 Mar 2026 13:03:19 +0900 Subject: [PATCH] =?UTF-8?q?RP=20=EA=B3=B5=EA=B0=9C=ED=82=A4=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EB=B0=8F=20Trusted=20RP=20=ED=8C=90=EC=A0=95=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/domain/hydra_models.go | 27 +++++++++++++ backend/internal/handler/dev_handler.go | 51 ++++++++++++++++--------- 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/backend/internal/domain/hydra_models.go b/backend/internal/domain/hydra_models.go index d0be623a..0923d9a4 100644 --- a/backend/internal/domain/hydra_models.go +++ b/backend/internal/domain/hydra_models.go @@ -12,9 +12,36 @@ type HydraClient struct { ResponseTypes []string `json:"response_types,omitempty"` Scope string `json:"scope,omitempty"` TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"` + JWKSUri string `json:"jwks_uri,omitempty"` + JWKS interface{} `json:"jwks,omitempty"` Metadata map[string]interface{} `json:"metadata,omitempty"` } +func (c *HydraClient) IsTrustedRP() bool { + // A Trusted RP must have a public key registered (URI or Inline) + // and use private_key_jwt for token endpoint authentication. + hasPublicKey := c.JWKSUri != "" || c.JWKS != nil + isPrivateKeyJwt := c.TokenEndpointAuthMethod == "private_key_jwt" + return hasPublicKey && isPrivateKeyJwt +} + +func (c *HydraClient) IsHeadlessLoginEnabled() bool { + if !c.IsTrustedRP() { + return false + } + if c.Metadata == nil { + return false + } + val, ok := c.Metadata["headless_login_enabled"] + if !ok { + return false + } + if b, ok := val.(bool); ok { + return b + } + return false +} + type HydraConsentRequest struct { Challenge string `json:"challenge"` RequestedScope []string `json:"requested_scope"` diff --git a/backend/internal/handler/dev_handler.go b/backend/internal/handler/dev_handler.go index 792f1ff2..fe93a76a 100644 --- a/backend/internal/handler/dev_handler.go +++ b/backend/internal/handler/dev_handler.go @@ -81,15 +81,18 @@ type devStatsResponse struct { } type clientSummary struct { - ID string `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - Status string `json:"status"` - 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"` + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Status string `json:"status"` + CreatedAt *time.Time `json:"createdAt,omitempty"` + RedirectURIs []string `json:"redirectUris"` + Scopes []string `json:"scopes"` + ClientSecret string `json:"clientSecret,omitempty"` + TokenEndpointAuthMethod string `json:"tokenEndpointAuthMethod,omitempty"` + JwksUri string `json:"jwksUri,omitempty"` + Jwks interface{} `json:"jwks,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` } type clientListResponse struct { @@ -139,6 +142,8 @@ type clientUpsertRequest struct { GrantTypes *[]string `json:"grantTypes"` ResponseTypes *[]string `json:"responseTypes"` TokenEndpointAuthMethod *string `json:"tokenEndpointAuthMethod"` + JwksUri *string `json:"jwksUri"` + Jwks interface{} `json:"jwks"` Metadata *map[string]interface{} `json:"metadata"` } @@ -895,6 +900,8 @@ func (h *DevHandler) CreateClient(c *fiber.Ctx) error { ResponseTypes: responseTypes, Scope: strings.Join(scopes, " "), TokenEndpointAuthMethod: tokenAuthMethod, + JWKSUri: valueOr(req.JwksUri, ""), + JWKS: req.Jwks, Metadata: metadata, } @@ -1046,8 +1053,13 @@ func (h *DevHandler) UpdateClient(c *fiber.Ctx) error { ResponseTypes: derefSlice(req.ResponseTypes, current.ResponseTypes), Scope: buildScope(valueOrSlice(req.Scopes, strings.Fields(current.Scope))), TokenEndpointAuthMethod: resolveTokenAuthMethod(tokenAuthMethod, current.TokenEndpointAuthMethod), + JWKSUri: valueOr(req.JwksUri, current.JWKSUri), + JWKS: req.Jwks, Metadata: metadata, } + if req.Jwks == nil { + updated.JWKS = current.JWKS + } if err := validateReservedSystemClientName(updated.ClientID, updated.ClientName); err != nil { return errorJSON(c, fiber.StatusForbidden, err.Error()) } @@ -1640,15 +1652,18 @@ func (h *DevHandler) mapClientSummary(client domain.HydraClient) clientSummary { } return clientSummary{ - ID: client.ClientID, - Name: name, - Type: clientType, - Status: status, - CreatedAt: createdAt, - RedirectURIs: client.RedirectURIs, - Scopes: scopes, - ClientSecret: clientSecret, - Metadata: client.Metadata, + ID: client.ClientID, + Name: name, + Type: clientType, + Status: status, + CreatedAt: createdAt, + RedirectURIs: client.RedirectURIs, + Scopes: scopes, + ClientSecret: clientSecret, + TokenEndpointAuthMethod: client.TokenEndpointAuthMethod, + JwksUri: client.JWKSUri, + Jwks: client.JWKS, + Metadata: client.Metadata, } }