forked from baron/baron-sso
린트 적용
This commit is contained in:
@@ -8,10 +8,10 @@ import (
|
|||||||
// BrokerUser is the standard user model used within Baron SSO business logic.
|
// BrokerUser is the standard user model used within Baron SSO business logic.
|
||||||
// It defines the canonical set of fields that must be supported by any underlying IDP.
|
// It defines the canonical set of fields that must be supported by any underlying IDP.
|
||||||
type BrokerUser struct {
|
type BrokerUser struct {
|
||||||
ID string `json:"id" required:"true"`
|
ID string `json:"id" required:"true"`
|
||||||
Email string `json:"email" required:"true"`
|
Email string `json:"email" required:"true"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
PhoneNumber string `json:"phone_number"`
|
PhoneNumber string `json:"phone_number"`
|
||||||
// Attributes stores custom user attributes.
|
// Attributes stores custom user attributes.
|
||||||
// The "required_keys" tag specifies which keys MUST be present in the IDP's schema support.
|
// The "required_keys" tag specifies which keys MUST be present in the IDP's schema support.
|
||||||
Attributes map[string]interface{} `json:"attributes" required_keys:"grade,department"`
|
Attributes map[string]interface{} `json:"attributes" required_keys:"grade,department"`
|
||||||
|
|||||||
@@ -35,14 +35,14 @@ const (
|
|||||||
statusSuccess = "success"
|
statusSuccess = "success"
|
||||||
|
|
||||||
// Durations
|
// Durations
|
||||||
defaultExpiration = 5 * time.Minute
|
defaultExpiration = 5 * time.Minute
|
||||||
signupStateExpiration = 10 * time.Minute
|
signupStateExpiration = 10 * time.Minute
|
||||||
signupBlockDuration = 10 * time.Minute
|
signupBlockDuration = 10 * time.Minute
|
||||||
maxSignupFailures = 5
|
maxSignupFailures = 5
|
||||||
emailCodeTTL = 5 * time.Minute
|
emailCodeTTL = 5 * time.Minute
|
||||||
smsCodeTTL = 3 * time.Minute
|
smsCodeTTL = 3 * time.Minute
|
||||||
prefixPwdResetToken = "pwdreset_token:"
|
prefixPwdResetToken = "pwdreset_token:"
|
||||||
pwdResetExpiration = 15 * time.Minute
|
pwdResetExpiration = 15 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuthHandler struct {
|
type AuthHandler struct {
|
||||||
@@ -165,12 +165,12 @@ func (h *AuthHandler) SendSignupEmailCode(c *fiber.Ctx) error {
|
|||||||
Code: code,
|
Code: code,
|
||||||
Verified: false,
|
Verified: false,
|
||||||
FailCount: 0, // Reset fail count on new code generation? Or keep it?
|
FailCount: 0, // Reset fail count on new code generation? Or keep it?
|
||||||
// Requirement says "Auth fail > 5 -> block". New code usually resets or continues?
|
// 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.
|
// 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.
|
// 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.
|
// If we are issuing a new code, it's a new attempt cycle usually.
|
||||||
// However, spamming "send code" is also an attack.
|
// However, spamming "send code" is also an attack.
|
||||||
// Let's keep FailCount if exists, or 0.
|
// Let's keep FailCount if exists, or 0.
|
||||||
ExpiresAt: time.Now().Add(emailCodeTTL).Unix(),
|
ExpiresAt: time.Now().Add(emailCodeTTL).Unix(),
|
||||||
}
|
}
|
||||||
if state != nil {
|
if state != nil {
|
||||||
@@ -312,10 +312,18 @@ func (h *AuthHandler) Signup(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
// Check complexity (at least 2 types: lower, upper, digit, special)
|
// Check complexity (at least 2 types: lower, upper, digit, special)
|
||||||
types := 0
|
types := 0
|
||||||
if strings.ContainsAny(req.Password, "abcdefghijklmnopqrstuvwxyz") { types++ }
|
if strings.ContainsAny(req.Password, "abcdefghijklmnopqrstuvwxyz") {
|
||||||
if strings.ContainsAny(req.Password, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") { types++ }
|
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, "0123456789") {
|
||||||
|
types++
|
||||||
|
}
|
||||||
|
if strings.ContainsAny(req.Password, "!@#$%^&*()_+-=[]{}|;:,.<>?") {
|
||||||
|
types++
|
||||||
|
}
|
||||||
if types < 2 {
|
if types < 2 {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Password must contain at least 2 types of characters (letters, numbers, symbols)"})
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Password must contain at least 2 types of characters (letters, numbers, symbols)"})
|
||||||
}
|
}
|
||||||
@@ -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"})
|
return c.Status(501).JSON(fiber.Map{"error": "Real email sending not implemented"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// --- User Profile Handlers ---
|
// --- User Profile Handlers ---
|
||||||
|
|
||||||
func (h *AuthHandler) formatPhoneForDisplay(phone string) string {
|
func (h *AuthHandler) formatPhoneForDisplay(phone string) string {
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ type AuditLogEntry struct {
|
|||||||
LoginIDs map[string]string // loginId and loginId_normalized
|
LoginIDs map[string]string // loginId and loginId_normalized
|
||||||
Token string // For reset tokens, magic link tokens
|
Token string // For reset tokens, magic link tokens
|
||||||
DescopeError string
|
DescopeError string
|
||||||
DescopeStatus int // Descope HTTP status
|
DescopeStatus int // Descope HTTP status
|
||||||
DescopeBody string // Descope response body (full raw)
|
DescopeBody string // Descope response body (full raw)
|
||||||
RefreshToken string
|
RefreshToken string
|
||||||
SessionJwt string
|
SessionJwt string
|
||||||
AccessJwt string
|
AccessJwt string
|
||||||
@@ -68,7 +68,6 @@ func NewAuditLogEntry(c *fiber.Ctx, stage string) *AuditLogEntry {
|
|||||||
headers["Origin"] = c.Get("Origin")
|
headers["Origin"] = c.Get("Origin")
|
||||||
headers["Referer"] = c.Get("Referer")
|
headers["Referer"] = c.Get("Referer")
|
||||||
|
|
||||||
|
|
||||||
return &AuditLogEntry{
|
return &AuditLogEntry{
|
||||||
RequestID: reqID,
|
RequestID: reqID,
|
||||||
Stage: stage,
|
Stage: stage,
|
||||||
@@ -85,7 +84,6 @@ func NewAuditLogEntry(c *fiber.Ctx, stage string) *AuditLogEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Log emits an audit log entry using slog.
|
// Log emits an audit log entry using slog.
|
||||||
// It includes common fields and allows for additional custom fields.
|
// It includes common fields and allows for additional custom fields.
|
||||||
func (ale *AuditLogEntry) Log(level slog.Level, msg string, args ...any) {
|
func (ale *AuditLogEntry) Log(level slog.Level, msg string, args ...any) {
|
||||||
|
|||||||
@@ -70,28 +70,28 @@ func (d *DescopeProvider) GetMetadata() (*domain.IDPMetadata, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DescopeProvider) InitiatePasswordReset(loginID, redirectUrl string) error {
|
func (d *DescopeProvider) InitiatePasswordReset(loginID, redirectUrl string) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
err := d.Client.Auth.Password().SendPasswordReset(ctx, loginID, redirectUrl, nil)
|
err := d.Client.Auth.Password().SendPasswordReset(ctx, loginID, redirectUrl, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Descope SendPasswordReset failed (raw)",
|
slog.Error("Descope SendPasswordReset failed (raw)",
|
||||||
"loginID", loginID,
|
"loginID", loginID,
|
||||||
"redirectUrl", redirectUrl,
|
"redirectUrl", redirectUrl,
|
||||||
"err", err,
|
"err", err,
|
||||||
"err_type", fmt.Sprintf("%T", err),
|
"err_type", fmt.Sprintf("%T", err),
|
||||||
)
|
)
|
||||||
|
|
||||||
if de, ok := err.(*descope.Error); ok {
|
if de, ok := err.(*descope.Error); ok {
|
||||||
status := de.Info[descope.ErrorInfoKeys.HTTPResponseStatusCode] // "Status-Code"
|
status := de.Info[descope.ErrorInfoKeys.HTTPResponseStatusCode] // "Status-Code"
|
||||||
slog.Error("Descope error details",
|
slog.Error("Descope error details",
|
||||||
"code", de.Code,
|
"code", de.Code,
|
||||||
"description", de.Description,
|
"description", de.Description,
|
||||||
"message", de.Message,
|
"message", de.Message,
|
||||||
"status_code", status,
|
"status_code", status,
|
||||||
"info", de.Info,
|
"info", de.Info,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DescopeProvider) VerifyPasswordResetToken(token string) (*domain.AuthInfo, error) {
|
func (d *DescopeProvider) VerifyPasswordResetToken(token string) (*domain.AuthInfo, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user