1
0
forked from baron/baron-sso

Merge branch 'dev' into feat/org-chart-rebac

This commit is contained in:
2026-02-24 12:42:02 +09:00
106 changed files with 3373 additions and 802 deletions

View File

@@ -4,6 +4,7 @@ import (
"baron-sso-backend/internal/domain"
"baron-sso-backend/internal/logger"
"baron-sso-backend/internal/repository"
"baron-sso-backend/internal/response"
"baron-sso-backend/internal/service"
"baron-sso-backend/internal/utils"
"bytes"
@@ -1562,13 +1563,12 @@ func (h *AuthHandler) PasswordLogin(c *fiber.Ctx) error {
ale.LatencyMs = time.Since(startTime)
ale.ProviderError = err.Error()
ale.Log(slog.LevelError, "Body parse error")
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
return response.Error(c, fiber.StatusBadRequest, "bad_request", "Invalid request body")
}
loginID := strings.TrimSpace(req.LoginID)
ale.LoginIDs["loginId"] = req.LoginID // 원문
ale.LoginIDs["loginId_normalized"] = loginID
ale.NewPassword = req.Password // For test only, logging password (sensitive)
ale.Log(slog.LevelInfo, "Attempting to login")
@@ -1577,22 +1577,22 @@ func (h *AuthHandler) PasswordLogin(c *fiber.Ctx) error {
ale.LatencyMs = time.Since(startTime)
ale.ProviderError = "IDP Provider is nil"
ale.Log(slog.LevelError, "IDP Provider is nil")
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Authentication service not configured"})
return response.Error(c, fiber.StatusInternalServerError, "service_unavailable", "Authentication service not configured")
}
authInfo, err := h.IdpProvider.SignIn(loginID, req.Password)
if err != nil {
if errors.Is(err, domain.ErrNotSupported) {
return c.Status(fiber.StatusNotImplemented).JSON(fiber.Map{"error": "Login method not supported"})
return response.Error(c, fiber.StatusNotImplemented, "not_supported", "Login method not supported")
}
ale.Status = fiber.StatusUnauthorized
ale.LatencyMs = time.Since(startTime)
ale.ProviderError = err.Error()
ale.Log(slog.LevelWarn, "IDP sign-in failed", slog.String("provider", h.IdpProvider.Name()))
if strings.Contains(err.Error(), "not found") || strings.Contains(err.Error(), "identity") {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not registered"})
return response.Error(c, fiber.StatusNotFound, "not_found", "User not registered")
}
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid credentials"})
return response.Error(c, fiber.StatusUnauthorized, "password_or_email_mismatch", "Invalid credentials")
}
subject, resolveErr := h.resolveKratosIdentityIDFromLoginID(c.Context(), loginID)
@@ -1604,7 +1604,6 @@ func (h *AuthHandler) PasswordLogin(c *fiber.Ctx) error {
ale.Status = fiber.StatusOK
ale.LatencyMs = time.Since(startTime)
ale.SessionJwt = authInfo.SessionToken.JWT
setSessionIDLocal(c, authInfo.SessionToken)
ale.Log(slog.LevelInfo, "Login successful", slog.String("provider", h.IdpProvider.Name()), slog.String("subject", authInfo.Subject))
@@ -1856,11 +1855,23 @@ func (h *AuthHandler) ProcessPasswordResetToken(c *fiber.Ctx) error {
ale.LoginIDs["loginId"] = loginID
ale.LoginIDs["loginId_normalized"] = loginID
redirectURL := fmt.Sprintf("%s/reset-password?loginId=%s&token=%s",
os.Getenv("USERFRONT_URL"),
loginID,
token,
)
userfrontURL := strings.TrimRight(os.Getenv("USERFRONT_URL"), "/")
if userfrontURL == "" {
userfrontURL = "https://sso.hmac.kr"
}
redirectBase, parseErr := url.Parse(userfrontURL + "/reset-password")
if parseErr != nil {
ale.Status = fiber.StatusInternalServerError
ale.LatencyMs = time.Since(startTime)
ale.ProviderError = parseErr.Error()
ale.Log(slog.LevelError, "Failed to compose reset redirect URL")
return c.Status(fiber.StatusInternalServerError).SendString("Failed to compose redirect URL")
}
query := redirectBase.Query()
query.Set("loginId", loginID)
query.Set("token", token)
redirectBase.RawQuery = query.Encode()
redirectURL := redirectBase.String()
ale.RedirectTo = redirectURL
ale.Status = fiber.StatusFound
@@ -1894,22 +1905,29 @@ func (h *AuthHandler) CompletePasswordReset(c *fiber.Ctx) error {
}
// loginID는 URL 쿼리 파라미터 또는 토큰 조회로 받습니다.
loginID := c.Query("loginId")
resetToken := c.Query("token")
if loginID == "" && resetToken != "" {
if val, err := h.RedisService.Get(prefixPwdResetToken + resetToken); err == nil && val != "" {
loginID = val
loginID := strings.TrimSpace(c.Query("loginId"))
resetToken := strings.TrimSpace(c.Query("token"))
if resetToken != "" {
val, err := h.RedisService.Get(prefixPwdResetToken + resetToken)
if err != nil || strings.TrimSpace(val) == "" {
ale.Status = fiber.StatusUnauthorized
ale.LatencyMs = time.Since(startTime)
ale.ProviderError = "Invalid or expired reset token"
ale.Token = resetToken
ale.Log(slog.LevelWarn, "Reset token invalid or expired")
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid or expired reset token"})
}
loginID = strings.TrimSpace(val)
ale.Token = resetToken
}
if loginID != "" && !strings.Contains(loginID, "@") {
loginID = normalizePhoneForLoginID(loginID)
}
ale.LoginIDs["loginId"] = loginID
ale.RequestBody = fmt.Sprintf("{\"newPassword\": \"%s\"}", req.NewPassword) // Log request body (for test only)
ale.NewPassword = req.NewPassword // Log new password (for test only)
// Request cookie logging (minimal)
// 요청 쿠키는 원문을 기록하지 않고 존재 여부만 기록합니다.
if cookieHeader := c.Get(fiber.HeaderCookie); cookieHeader != "" {
ale.Headers["Request-Cookie-Header"] = cookieHeader
if dsrfCookie := c.Cookies("DSRF"); dsrfCookie != "" {
ale.ParsedCookieDSRF = dsrfCookie
ale.HasCookieDSRF = true
@@ -1926,7 +1944,7 @@ func (h *AuthHandler) CompletePasswordReset(c *fiber.Ctx) error {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Login ID and new password are required"})
}
// 디버깅을 위해 요청된 새 비밀번호를 로그로 출력
// 새 비밀번호 값은 기록하지 않고, 요청 수신 이벤트만 남깁니다.
ale.Log(slog.LevelInfo, "Received new password for reset")
policy := h.resolvePasswordPolicy()