From 0ac5b9ee0a3c162ca6e5e040cddd5b38cb8c67a1 Mon Sep 17 00:00:00 2001 From: kyy Date: Tue, 3 Feb 2026 11:30:41 +0900 Subject: [PATCH] =?UTF-8?q?scope=20=EC=84=A4=EB=AA=85/=ED=95=84=EC=88=98?= =?UTF-8?q?=20=EC=97=AC=EB=B6=80=20=EB=B0=8F=20grant=5Fscope=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/handler/auth_handler.go | 137 +++++++++++++++++++++-- 1 file changed, 125 insertions(+), 12 deletions(-) diff --git a/backend/internal/handler/auth_handler.go b/backend/internal/handler/auth_handler.go index cbd9394a..974755c1 100644 --- a/backend/internal/handler/auth_handler.go +++ b/backend/internal/handler/auth_handler.go @@ -3318,26 +3318,133 @@ func (h *AuthHandler) GetConsentRequest(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusInternalServerError, "Failed to get consent information") } - return c.JSON(consentRequest) + // Hydra 응답을 기본으로 하되, 메타데이터에서 커스텀 스코프 설명을 추출하여 추가 + response := fiber.Map{ + "challenge": consentRequest.Challenge, + "requested_scope": consentRequest.RequestedScope, + "requested_access_token_audience": consentRequest.RequestedAudience, + "skip": consentRequest.Skip, + "subject": consentRequest.Subject, + "client": consentRequest.Client, + } + + // structured_scopes 파싱 및 scope_details 생성 + if metadata := consentRequest.Client.Metadata; metadata != nil { + if rawScopes, ok := metadata["structured_scopes"]; ok { + scopeDetails := make(map[string]map[string]interface{}) + + // JSON 언마샬링 등을 통해 map[string]interface{} 또는 []interface{}로 들어옴 + // 안전하게 처리 + rawBytes, _ := json.Marshal(rawScopes) + var scopesList []map[string]interface{} + if err := json.Unmarshal(rawBytes, &scopesList); err == nil { + for _, item := range scopesList { + name, _ := item["name"].(string) + if name == "" { + continue + } + desc, _ := item["description"].(string) + mandatory, _ := item["mandatory"].(bool) + + scopeDetails[name] = map[string]interface{}{ + "description": desc, + "mandatory": mandatory, + } + } + } + response["scope_details"] = scopeDetails + } + } + + return c.JSON(response) } +// AcceptConsentRequest - 프론트엔드에서 동의한 내용을 바탕으로 Hydra에 승인을 요청합니다. + func (h *AuthHandler) AcceptConsentRequest(c *fiber.Ctx) error { + var req struct { - ConsentChallenge string `json:"consent_challenge"` - } - if err := c.BodyParser(&req); err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") - } - if req.ConsentChallenge == "" { - return fiber.NewError(fiber.StatusBadRequest, "consent_challenge is required") + + ConsentChallenge string `json:"consent_challenge"` + + GrantScope []string `json:"grant_scope"` // 사용자가 선택한 스코프 + } - consentRequest, err := h.Hydra.GetConsentRequest(c.Context(), req.ConsentChallenge) - if err != nil { - slog.Error("failed to get hydra consent request before accepting", "error", err) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get consent information") + + + if err := c.BodyParser(&req); err != nil { + + return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") + } + + + if req.ConsentChallenge == "" { + + return fiber.NewError(fiber.StatusBadRequest, "consent_challenge is required") + + } + + + + // 1. Hydra에서 원래 요청 정보 조회 + + consentRequest, err := h.Hydra.GetConsentRequest(c.Context(), req.ConsentChallenge) + + if err != nil { + + slog.Error("failed to get hydra consent request before accepting", "error", err) + + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get consent information") + + } + + + + // 2. 스코프 필터링 (사용자가 선택한 것만 허용) + + // 만약 프론트엔드에서 grant_scope를 보내지 않았다면(기존 동작), 전체 허용으로 간주하거나 에러 처리. + + // 여기서는 명시적으로 보낸 경우에만 필터링하고, 없으면 다 승인(하위 호환)하도록 함. + + if len(req.GrantScope) > 0 { + + // 유효성 검증: 사용자가 선택한 스코프가 실제로 요청된 스코프에 포함되는지 확인 + + allowedScopes := make(map[string]bool) + + for _, s := range consentRequest.RequestedScope { + + allowedScopes[s] = true + + } + + + + filteredScopes := make([]string, 0, len(req.GrantScope)) + + for _, s := range req.GrantScope { + + if allowedScopes[s] { + + filteredScopes = append(filteredScopes, s) + + } + + } + + // 덮어씌우기 (Hydra 서비스는 이 필드를 grant_scope로 사용함) + + consentRequest.RequestedScope = filteredScopes + + } + + + + // 3. Hydra에 승인 요청 + if consentRequest.Subject == "" { return fiber.NewError(fiber.StatusInternalServerError, "Consent subject missing") } @@ -3356,11 +3463,17 @@ func (h *AuthHandler) AcceptConsentRequest(c *fiber.Ctx) error { acceptResp, err := h.Hydra.AcceptConsentRequest(c.Context(), req.ConsentChallenge, consentRequest, sessionClaims) if err != nil { + slog.Error("failed to accept hydra consent request", "error", err) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to accept consent request") + } + + return c.JSON(acceptResp) + } func (h *AuthHandler) AcceptOidcLoginRequest(c *fiber.Ctx) error {