forked from baron/baron-sso
Merge branch 'dev' into feat/id_login
This commit is contained in:
@@ -66,19 +66,21 @@ 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:"
|
||||
headlessJWKSFetchTTL = 5 * time.Second
|
||||
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
|
||||
)
|
||||
|
||||
type AuthHandler struct {
|
||||
@@ -1741,14 +1743,14 @@ func containsHeadlessAudience(expected []string, actual headlessAssertionAud) bo
|
||||
func (h *AuthHandler) loadHeadlessJWKS(ctx context.Context, client domain.HydraClient) (*jose.JSONWebKeySet, error) {
|
||||
var raw []byte
|
||||
switch {
|
||||
case client.JWKS != nil:
|
||||
data, err := json.Marshal(client.JWKS)
|
||||
case client.HeadlessJWKS() != nil:
|
||||
data, err := json.Marshal(client.HeadlessJWKS())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode jwks: %w", err)
|
||||
}
|
||||
raw = data
|
||||
case strings.TrimSpace(client.JWKSUri) != "":
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, strings.TrimSpace(client.JWKSUri), nil)
|
||||
case client.HeadlessJWKSURI() != "":
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, client.HeadlessJWKSURI(), nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build jwks request: %w", err)
|
||||
}
|
||||
@@ -1768,7 +1770,7 @@ func (h *AuthHandler) loadHeadlessJWKS(ctx context.Context, client domain.HydraC
|
||||
}
|
||||
raw = body
|
||||
default:
|
||||
return nil, fmt.Errorf("trusted rp public key is not configured")
|
||||
return nil, fmt.Errorf("headless login public key is not configured")
|
||||
}
|
||||
|
||||
var keySet jose.JSONWebKeySet
|
||||
@@ -1776,7 +1778,7 @@ func (h *AuthHandler) loadHeadlessJWKS(ctx context.Context, client domain.HydraC
|
||||
return nil, fmt.Errorf("failed to decode jwks: %w", err)
|
||||
}
|
||||
if len(keySet.Keys) == 0 {
|
||||
return nil, fmt.Errorf("trusted rp jwks has no keys")
|
||||
return nil, fmt.Errorf("headless login jwks has no keys")
|
||||
}
|
||||
return &keySet, nil
|
||||
}
|
||||
@@ -2410,9 +2412,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)
|
||||
@@ -2432,7 +2434,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")
|
||||
@@ -2498,6 +2500,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")
|
||||
@@ -2551,6 +2556,9 @@ func (h *AuthHandler) ProcessPasswordResetToken(c *fiber.Ctx) error {
|
||||
token = c.Query("t")
|
||||
}
|
||||
}
|
||||
if token == "" {
|
||||
token = c.Params("token")
|
||||
}
|
||||
ale.Token = token
|
||||
|
||||
if token == "" {
|
||||
@@ -2625,6 +2633,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"
|
||||
@@ -2694,6 +2710,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."})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user