forked from baron/baron-sso
린트 적용
This commit is contained in:
@@ -35,14 +35,14 @@ const (
|
||||
statusSuccess = "success"
|
||||
|
||||
// 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
|
||||
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
|
||||
)
|
||||
|
||||
type AuthHandler struct {
|
||||
@@ -125,12 +125,12 @@ func (h *AuthHandler) CheckEmail(c *fiber.Ctx) error {
|
||||
// Note: Descope doesn't have a direct "exists" check, we use Load or Search.
|
||||
// Since we are checking availability for signup, we want "User not found".
|
||||
exists, err := h.DescopeClient.Management.User().Load(context.Background(), req.Email)
|
||||
|
||||
|
||||
// If err is nil and exists is not nil, user exists.
|
||||
if err == nil && exists != nil {
|
||||
return c.JSON(fiber.Map{"available": false, "message": "Email already registered"})
|
||||
}
|
||||
|
||||
|
||||
// Check if specific error is "not found" or just assume if Load fails it might be free.
|
||||
// Typically Descope Load returns error if not found? Let's assume so or check error message.
|
||||
// Actually, strictly speaking, we should handle specific errors, but for MVP:
|
||||
@@ -146,7 +146,7 @@ func (h *AuthHandler) SendSignupEmailCode(c *fiber.Ctx) error {
|
||||
req.Type = "email" // Enforce type
|
||||
|
||||
key := prefixSignupEmail + req.Target
|
||||
|
||||
|
||||
// 1. Check existing state (Rate Limit / Block)
|
||||
state, _ := h.getSignupState(key)
|
||||
if state != nil && state.FailCount > maxSignupFailures {
|
||||
@@ -164,26 +164,26 @@ func (h *AuthHandler) SendSignupEmailCode(c *fiber.Ctx) error {
|
||||
newState := &signupState{
|
||||
Code: code,
|
||||
Verified: false,
|
||||
FailCount: 0, // Reset fail count on new code generation? Or keep it?
|
||||
// Requirement says "Auth fail > 5 -> block". New code usually resets or continues?
|
||||
// Usually getting a new code doesn't reset verify failure count if we want strict blocking.
|
||||
// But for simplicity let's say "fail count" applies to verification attempts.
|
||||
// If we are issuing a new code, it's a new attempt cycle usually.
|
||||
// However, spamming "send code" is also an attack.
|
||||
// Let's keep FailCount if exists, or 0.
|
||||
FailCount: 0, // Reset fail count on new code generation? Or keep it?
|
||||
// Requirement says "Auth fail > 5 -> block". New code usually resets or continues?
|
||||
// Usually getting a new code doesn't reset verify failure count if we want strict blocking.
|
||||
// But for simplicity let's say "fail count" applies to verification attempts.
|
||||
// If we are issuing a new code, it's a new attempt cycle usually.
|
||||
// However, spamming "send code" is also an attack.
|
||||
// Let's keep FailCount if exists, or 0.
|
||||
ExpiresAt: time.Now().Add(emailCodeTTL).Unix(),
|
||||
}
|
||||
if state != nil {
|
||||
newState.FailCount = state.FailCount
|
||||
}
|
||||
|
||||
|
||||
h.saveSignupState(key, newState, signupStateExpiration)
|
||||
|
||||
// 4. Send Email
|
||||
if h.EmailService == nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Email service not configured"})
|
||||
}
|
||||
|
||||
|
||||
subject := "[Baron SSO] 회원가입 인증코드"
|
||||
body := fmt.Sprintf(`
|
||||
<div style="padding: 20px; font-family: sans-serif;">
|
||||
@@ -193,7 +193,7 @@ func (h *AuthHandler) SendSignupEmailCode(c *fiber.Ctx) error {
|
||||
<p>이 코드는 5분간 유효합니다.</p>
|
||||
</div>
|
||||
`, code)
|
||||
|
||||
|
||||
go h.EmailService.SendEmail(req.Target, subject, body)
|
||||
|
||||
return c.JSON(fiber.Map{"message": "Verification code sent"})
|
||||
@@ -312,10 +312,18 @@ func (h *AuthHandler) Signup(c *fiber.Ctx) error {
|
||||
}
|
||||
// Check complexity (at least 2 types: lower, upper, digit, special)
|
||||
types := 0
|
||||
if strings.ContainsAny(req.Password, "abcdefghijklmnopqrstuvwxyz") { types++ }
|
||||
if strings.ContainsAny(req.Password, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") { types++ }
|
||||
if strings.ContainsAny(req.Password, "0123456789") { types++ }
|
||||
if strings.ContainsAny(req.Password, "!@#$%^&*()_+-=[]{}|;:,.<>?") { types++ }
|
||||
if strings.ContainsAny(req.Password, "abcdefghijklmnopqrstuvwxyz") {
|
||||
types++
|
||||
}
|
||||
if strings.ContainsAny(req.Password, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") {
|
||||
types++
|
||||
}
|
||||
if strings.ContainsAny(req.Password, "0123456789") {
|
||||
types++
|
||||
}
|
||||
if strings.ContainsAny(req.Password, "!@#$%^&*()_+-=[]{}|;:,.<>?") {
|
||||
types++
|
||||
}
|
||||
if types < 2 {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Password must contain at least 2 types of characters (letters, numbers, symbols)"})
|
||||
}
|
||||
@@ -359,14 +367,14 @@ func (h *AuthHandler) Signup(c *fiber.Ctx) error {
|
||||
"termsAccepted": req.TermsAccepted,
|
||||
"createdAt": time.Now().Format(time.RFC3339),
|
||||
}
|
||||
|
||||
|
||||
// Create user
|
||||
// Note: Descope `Create` does not set password. We usually need `Create` then `Password().Update`
|
||||
// or use a specialized signup flow.
|
||||
// Note: Descope `Create` does not set password. We usually need `Create` then `Password().Update`
|
||||
// or use a specialized signup flow.
|
||||
// `Management.User().Create` creates a user but doesn't set a password credential immediately unless specified?
|
||||
// Actually `User().Create` creates the identity.
|
||||
// To set password, we use `h.DescopeClient.Management.User().SetPassword(...)`
|
||||
|
||||
|
||||
// Check if user exists (Double check)
|
||||
exists, _ := h.DescopeClient.Management.User().Load(context.Background(), req.Email)
|
||||
if exists != nil {
|
||||
@@ -1305,8 +1313,6 @@ func (h *AuthHandler) HandleDescopeEmailRelay(c *fiber.Ctx) error {
|
||||
return c.Status(501).JSON(fiber.Map{"error": "Real email sending not implemented"})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --- User Profile Handlers ---
|
||||
|
||||
func (h *AuthHandler) formatPhoneForDisplay(phone string) string {
|
||||
@@ -1409,7 +1415,7 @@ func (h *AuthHandler) UpdateMe(c *fiber.Ctx) error {
|
||||
// Normalize for comparison
|
||||
normID := strings.ReplaceAll(loginID, "+82", "0")
|
||||
normOld := strings.ReplaceAll(oldPhoneStorage, "+82", "0")
|
||||
|
||||
|
||||
if loginID == oldPhoneStorage || (normOld != "" && normID == normOld) {
|
||||
slog.Info("[UpdateMe] Updating LoginID", "old", loginID, "new", newPhoneStorage)
|
||||
_, err = h.DescopeClient.Management.User().UpdateLoginID(c.Context(), loginID, newPhoneStorage)
|
||||
@@ -1456,7 +1462,7 @@ func (h *AuthHandler) SendUpdateCode(c *fiber.Ctx) error {
|
||||
if token == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Unauthorized"})
|
||||
}
|
||||
|
||||
|
||||
authorized, userToken, err := h.DescopeClient.Auth.ValidateSessionWithToken(c.Context(), token)
|
||||
if err != nil || !authorized {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid session"})
|
||||
@@ -1471,7 +1477,7 @@ func (h *AuthHandler) SendUpdateCode(c *fiber.Ctx) error {
|
||||
|
||||
phone := h.formatPhoneForStorage(req.Phone)
|
||||
code := fmt.Sprintf("%06d", rand.Intn(1000000))
|
||||
|
||||
|
||||
// Store code in Redis
|
||||
key := "otp_update_phone:" + userToken.ID + ":" + phone
|
||||
h.RedisService.Set(key, code, 5*time.Minute)
|
||||
|
||||
Reference in New Issue
Block a user