1
0
forked from baron/baron-sso

비밀번호 재설정 중복 완료 요청 문제 수정

This commit is contained in:
2026-03-31 11:17:55 +09:00
parent df145b2957
commit 68114eea66
8 changed files with 309 additions and 31 deletions

View File

@@ -66,18 +66,20 @@ const (
loginFlowLink = "link"
// Durations
defaultExpiration = 5 * time.Minute
signupStateExpiration = 10 * time.Minute
signupBlockDuration = 10 * time.Minute
maxSignupFailures = 5
emailCodeTTL = 5 * time.Minute
smsCodeTTL = 3 * time.Minute
prefixPwdResetToken = "pwdreset_token:"
pwdResetExpiration = 15 * time.Minute
minPollInterval = 2 * time.Second
loginCodeExpiration = 10 * time.Minute
linkResendCooldown = 60 * time.Second
prefixDrySend = "dry_send:"
defaultExpiration = 5 * time.Minute
signupStateExpiration = 10 * time.Minute
signupBlockDuration = 10 * time.Minute
maxSignupFailures = 5
emailCodeTTL = 5 * time.Minute
smsCodeTTL = 3 * time.Minute
prefixPwdResetToken = "pwdreset_token:"
prefixPwdResetUsed = "pwdreset_used:"
pwdResetExpiration = 15 * time.Minute
pwdResetUsedExpiration = 2 * time.Minute
minPollInterval = 2 * time.Second
loginCodeExpiration = 10 * time.Minute
linkResendCooldown = 60 * time.Second
prefixDrySend = "dry_send:"
headlessJWKSFetchTTL = 5 * time.Second
)
@@ -2368,9 +2370,9 @@ func (h *AuthHandler) InitiatePasswordReset(c *fiber.Ctx) error {
}
userfrontURL := h.resolveUserfrontURL(c)
// [Changed] Point to Backend API for verification (which then redirects to Frontend)
redirectURL := fmt.Sprintf("%s/api/v1/auth/password/reset/verify", userfrontURL)
ale.RedirectTo = redirectURL
// 비밀번호 재설정 링크는 backend verify 엔드포인트를 거쳐서 userfront로 이동합니다.
// 이렇게 해야 메일/SMS 링크 프리뷰나 자동 스캔으로 토큰이 직접 노출되는 경로를 줄일 수 있습니다.
verifyBaseURL := fmt.Sprintf("%s/api/v1/auth/password/reset/v", userfrontURL)
// 내부 토큰 발급 + 우리 채널로 전송
resetToken := GenerateSecureToken(32)
@@ -2390,7 +2392,7 @@ func (h *AuthHandler) InitiatePasswordReset(c *fiber.Ctx) error {
return errorJSON(c, fiber.StatusInternalServerError, "Failed to store reset token")
}
resetLink := fmt.Sprintf("%s/reset-password?token=%s", userfrontURL, resetToken)
resetLink := fmt.Sprintf("%s/%s", verifyBaseURL, resetToken)
ale.RedirectTo = resetLink
ale.Operation = "SendPasswordReset"
ale.Log(slog.LevelInfo, "Initiating password reset via internal token")
@@ -2456,6 +2458,9 @@ func (h *AuthHandler) VerifyPasswordResetPage(c *fiber.Ctx) error {
if token == "" {
token = c.Query("t")
}
if token == "" {
token = c.Params("token")
}
if token == "" {
return c.Status(fiber.StatusBadRequest).SendString("Missing token")
@@ -2509,6 +2514,9 @@ func (h *AuthHandler) ProcessPasswordResetToken(c *fiber.Ctx) error {
token = c.Query("t")
}
}
if token == "" {
token = c.Params("token")
}
ale.Token = token
if token == "" {
@@ -2583,6 +2591,14 @@ func (h *AuthHandler) CompletePasswordReset(c *fiber.Ctx) error {
if resetToken != "" {
val, err := h.RedisService.Get(prefixPwdResetToken + resetToken)
if err != nil || strings.TrimSpace(val) == "" {
if usedLoginID, usedErr := h.RedisService.Get(prefixPwdResetUsed + resetToken); usedErr == nil && strings.TrimSpace(usedLoginID) != "" {
ale.Status = fiber.StatusOK
ale.LatencyMs = time.Since(startTime)
ale.Token = resetToken
ale.LoginIDs["loginId"] = strings.TrimSpace(usedLoginID)
ale.Log(slog.LevelInfo, "Duplicate reset completion ignored after successful use")
return c.JSON(fiber.Map{"message": "Password has been reset successfully."})
}
ale.Status = fiber.StatusUnauthorized
ale.LatencyMs = time.Since(startTime)
ale.ProviderError = "Invalid or expired reset token"
@@ -2652,6 +2668,7 @@ func (h *AuthHandler) CompletePasswordReset(c *fiber.Ctx) error {
ale.Log(slog.LevelInfo, "Password updated successfully", slog.String("login_id", loginID))
if resetToken != "" {
_ = h.RedisService.Delete(prefixPwdResetToken + resetToken)
_ = h.RedisService.Set(prefixPwdResetUsed+resetToken, loginID, pwdResetUsedExpiration)
}
return c.JSON(fiber.Map{"message": "Password has been reset successfully."})
}