diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 35215a82..a51bea25 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -535,6 +535,7 @@ func main() { user.Post("/me/send-code", authHandler.SendUpdateCode) user.Post("/me/verify-code", authHandler.VerifyUpdateCode) user.Get("/rp/linked", authHandler.ListLinkedRps) + user.Delete("/rp/linked/:id", authHandler.RevokeLinkedRp) // Admin Routes admin := api.Group("/admin") diff --git a/backend/internal/handler/auth_handler.go b/backend/internal/handler/auth_handler.go index b9dda06d..b69353ff 100644 --- a/backend/internal/handler/auth_handler.go +++ b/backend/internal/handler/auth_handler.go @@ -3306,6 +3306,35 @@ func (h *AuthHandler) ListLinkedRps(c *fiber.Ctx) error { return c.JSON(linkedRpListResponse{Items: items}) } +func (h *AuthHandler) RevokeLinkedRp(c *fiber.Ctx) error { + clientID := c.Params("id") + if clientID == "" { + return fiber.NewError(fiber.StatusBadRequest, "client_id is required") + } + + subject, err := h.resolveConsentSubject(c) + if err != nil || subject == "" { + return fiber.NewError(fiber.StatusUnauthorized, "Authentication required") + } + + slog.Info("RevokeLinkedRp called", "subject", subject, "client_id", clientID) + + if h.Hydra == nil { + return fiber.NewError(fiber.StatusServiceUnavailable, "hydra admin unavailable") + } + + // Hydra에서 해당 사용자와 클라이언트의 모든 동의 세션을 삭제 + if err := h.Hydra.RevokeConsentSessions(c.Context(), subject, clientID); err != nil { + slog.Error("failed to revoke hydra consent sessions", "error", err) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to revoke link") + } + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "status": "success", + "message": "Link revoked successfully", + }) +} + func (h *AuthHandler) GetConsentRequest(c *fiber.Ctx) error { challenge := c.Query("consent_challenge") if challenge == "" {