diff --git a/backend/internal/domain/auth_models.go b/backend/internal/domain/auth_models.go index 89992130..cc24e903 100644 --- a/backend/internal/domain/auth_models.go +++ b/backend/internal/domain/auth_models.go @@ -2,13 +2,13 @@ package domain type EnchantedLinkInitRequest struct { LoginID string `json:"loginId"` - URI string `json:"uri,omitempty"` // Redirect URI (optional for polling flow) + URI string `json:"uri,omitempty"` // Redirect URI (optional for polling flow) Method string `json:"method,omitempty"` // "email" or "sms" } type EnchantedLinkInitResponse struct { - LinkID string `json:"linkId"` - PendingRef string `json:"pendingRef"` + LinkID string `json:"linkId"` + PendingRef string `json:"pendingRef"` MaskedEmail string `json:"maskedEmail"` } @@ -30,4 +30,4 @@ type QRInitResponse struct { QRCode string `json:"qrCode"` // Base64 or URL PendingRef string `json:"pendingRef"` ExpiresIn int `json:"expiresIn"` -} \ No newline at end of file +} diff --git a/backend/internal/domain/models.go b/backend/internal/domain/models.go index 49ecccf8..d1962ceb 100644 --- a/backend/internal/domain/models.go +++ b/backend/internal/domain/models.go @@ -6,14 +6,14 @@ import ( // AuditLog represents a single audit event type AuditLog struct { - Timestamp time.Time `json:"timestamp"` - UserID string `json:"user_id"` - EventType string `json:"event_type"` // e.g., "login_success", "login_failed", "otp_sent" - Status string `json:"status"` // e.g., "success", "failure" - IPAddress string `json:"ip_address"` - UserAgent string `json:"user_agent"` - DeviceID string `json:"device_id,omitempty"` - Details string `json:"details,omitempty"` // JSON string or simple text + Timestamp time.Time `json:"timestamp"` + UserID string `json:"user_id"` + EventType string `json:"event_type"` // e.g., "login_success", "login_failed", "otp_sent" + Status string `json:"status"` // e.g., "success", "failure" + IPAddress string `json:"ip_address"` + UserAgent string `json:"user_agent"` + DeviceID string `json:"device_id,omitempty"` + Details string `json:"details,omitempty"` // JSON string or simple text } // AuditRepository defines interface for storing logs diff --git a/backend/internal/domain/sms_models.go b/backend/internal/domain/sms_models.go index d020db78..53956273 100644 --- a/backend/internal/domain/sms_models.go +++ b/backend/internal/domain/sms_models.go @@ -7,12 +7,12 @@ type SmsService interface { // NaverSmsRequest represents the request body for the Naver Cloud SMS API. type NaverSmsRequest struct { - Type string `json:"type"` - ContentType string `json:"contentType"` - CountryCode string `json:"countryCode"` - From string `json:"from"` - Content string `json:"content"` - Messages []SmsMessage `json:"messages"` + Type string `json:"type"` + ContentType string `json:"contentType"` + CountryCode string `json:"countryCode"` + From string `json:"from"` + Content string `json:"content"` + Messages []SmsMessage `json:"messages"` } // SmsMessage represents a single message to be sent. @@ -23,10 +23,10 @@ type SmsMessage struct { // NaverSmsResponse represents the response from the Naver Cloud SMS API. type NaverSmsResponse struct { - RequestID string `json:"requestId"` - RequestTime string `json:"requestTime"` - StatusCode string `json:"statusCode"` - StatusName string `json:"statusName"` + RequestID string `json:"requestId"` + RequestTime string `json:"requestTime"` + StatusCode string `json:"statusCode"` + StatusName string `json:"statusName"` } // SmsRequest represents the request body for sending an SMS. diff --git a/backend/internal/handler/admin_handler.go b/backend/internal/handler/admin_handler.go index 4c68e7bc..a324dfd4 100644 --- a/backend/internal/handler/admin_handler.go +++ b/backend/internal/handler/admin_handler.go @@ -3,9 +3,9 @@ package handler import ( "context" "log/slog" + "net/url" "os" "strings" - "net/url" "github.com/descope/go-sdk/descope" "github.com/descope/go-sdk/descope/client" @@ -50,7 +50,7 @@ func (h *AdminHandler) checkAuth(c *fiber.Ctx) error { if adminPass == "" { adminPass = "admin" // Default fallback } - + reqPass := c.Get("X-Admin-Password") if reqPass != adminPass { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid Admin Password"}) @@ -59,11 +59,11 @@ func (h *AdminHandler) checkAuth(c *fiber.Ctx) error { } type CreateUserRequest struct { - LoginID string `json:"loginId"` - Email string `json:"email"` - Phone string `json:"phone"` - DisplayName string `json:"displayName"` - Roles []string `json:"roles"` + LoginID string `json:"loginId"` + Email string `json:"email"` + Phone string `json:"phone"` + DisplayName string `json:"displayName"` + Roles []string `json:"roles"` Tenants map[string][]string `json:"tenants"` // tenantId -> roles } @@ -76,18 +76,20 @@ func (h *AdminHandler) CheckAuth(c *fiber.Ctx) error { // ListUsers - GET /api/v1/admin/users func (h *AdminHandler) ListUsers(c *fiber.Ctx) error { - if err := h.checkAuth(c); err != nil { return err } + if err := h.checkAuth(c); err != nil { + return err + } text := c.Query("text") - // Limit is not directly supported in SearchAll options as a simple int in all SDK versions, + // Limit is not directly supported in SearchAll options as a simple int in all SDK versions, // but let's check the options struct. // Based on previous inspection: SearchAll takes UserSearchOptions. - + var users []*descope.UserResponse var err error if text != "" { - options := &descope.UserSearchOptions{ Text: text, Limit: 50 } + options := &descope.UserSearchOptions{Text: text, Limit: 50} users, _, err = h.DescopeClient.Management.User().SearchAll(context.Background(), options) } else { // Nil options means default search (usually returns all or default page) @@ -104,13 +106,15 @@ func (h *AdminHandler) ListUsers(c *fiber.Ctx) error { // DeleteUser - DELETE /api/v1/admin/users/:loginId func (h *AdminHandler) DeleteUser(c *fiber.Ctx) error { - if err := h.checkAuth(c); err != nil { return err } + if err := h.checkAuth(c); err != nil { + return err + } loginID := c.Params("loginId") - // Decode if necessary (Fiber usually decodes params, but let's be safe if it's double encoded) - if decoded, err := url.QueryUnescape(loginID); err == nil { - loginID = decoded - } + // Decode if necessary (Fiber usually decodes params, but let's be safe if it's double encoded) + if decoded, err := url.QueryUnescape(loginID); err == nil { + loginID = decoded + } slog.Info("[Admin] Deleting user", "loginID", loginID) if err := h.DescopeClient.Management.User().Delete(context.Background(), loginID); err != nil { @@ -123,12 +127,14 @@ func (h *AdminHandler) DeleteUser(c *fiber.Ctx) error { // UpdateUserStatus - PATCH /api/v1/admin/users/:loginId/status func (h *AdminHandler) UpdateUserStatus(c *fiber.Ctx) error { - if err := h.checkAuth(c); err != nil { return err } + if err := h.checkAuth(c); err != nil { + return err + } loginID := c.Params("loginId") - if decoded, err := url.QueryUnescape(loginID); err == nil { - loginID = decoded - } + if decoded, err := url.QueryUnescape(loginID); err == nil { + loginID = decoded + } var req struct { Status string `json:"status"` // "enabled" or "disabled" @@ -161,7 +167,9 @@ func (h *AdminHandler) UpdateUserStatus(c *fiber.Ctx) error { // UpdateUser - PATCH /api/v1/admin/users/:loginId func (h *AdminHandler) UpdateUser(c *fiber.Ctx) error { - if err := h.checkAuth(c); err != nil { return err } + if err := h.checkAuth(c); err != nil { + return err + } loginID := c.Params("loginId") if decoded, err := url.QueryUnescape(loginID); err == nil { @@ -213,7 +221,9 @@ func (h *AdminHandler) UpdateUser(c *fiber.Ctx) error { } func (h *AdminHandler) CreateUser(c *fiber.Ctx) error { - if err := h.checkAuth(c); err != nil { return err } + if err := h.checkAuth(c); err != nil { + return err + } if h.DescopeClient == nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Descope Client not configured"}) @@ -247,7 +257,7 @@ func (h *AdminHandler) CreateUser(c *fiber.Ctx) error { VerifiedEmail: boolPtr(req.Email != ""), VerifiedPhone: boolPtr(normalizedPhone != ""), } - + // Add Roles if provided if len(req.Roles) > 0 { userObj.Roles = req.Roles @@ -278,4 +288,4 @@ func (h *AdminHandler) CreateUser(c *fiber.Ctx) error { "message": "User created successfully", "user": res, }) -} \ No newline at end of file +} diff --git a/backend/internal/handler/auth_handler.go b/backend/internal/handler/auth_handler.go index 55eabd09..cdf5ba97 100644 --- a/backend/internal/handler/auth_handler.go +++ b/backend/internal/handler/auth_handler.go @@ -55,34 +55,34 @@ func NewAuthHandler(redisService *service.RedisService) *AuthHandler { var descopeClient *client.DescopeClient var err error - if projectID != "" { - descopeClient, err = client.NewWithConfig(&client.Config{ - ProjectID: projectID, - ManagementKey: managementKey, - }) - if err != nil { - slog.Warn("Failed to initialize Descope Client", "error", err) - } - } - - return &AuthHandler{ - ProjectID: projectID, - SmsService: service.NewSmsService(), - EmailService: service.NewEmailService(), - RedisService: redisService, - DescopeClient: descopeClient, + if projectID != "" { + descopeClient, err = client.NewWithConfig(&client.Config{ + ProjectID: projectID, + ManagementKey: managementKey, + }) + if err != nil { + slog.Warn("Failed to initialize Descope Client", "error", err) } } - - // SendSms sends a verification code via SMS. (Restored for completeness) - func (h *AuthHandler) SendSms(c *fiber.Ctx) error { - var req domain.SmsRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"}) - } - - slog.Info("[SMS] Sending code", "phoneNumber", req.PhoneNumber) - sanitizedPhone := strings.ReplaceAll(req.PhoneNumber, "-", "") + + return &AuthHandler{ + ProjectID: projectID, + SmsService: service.NewSmsService(), + EmailService: service.NewEmailService(), + RedisService: redisService, + DescopeClient: descopeClient, + } +} + +// SendSms sends a verification code via SMS. (Restored for completeness) +func (h *AuthHandler) SendSms(c *fiber.Ctx) error { + var req domain.SmsRequest + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"}) + } + + slog.Info("[SMS] Sending code", "phoneNumber", req.PhoneNumber) + sanitizedPhone := strings.ReplaceAll(req.PhoneNumber, "-", "") rand.Seed(time.Now().UnixNano()) code := fmt.Sprintf("%06d", rand.Intn(1000000)) content := fmt.Sprintf("[Baron SSO] 인증번호: %s", code) diff --git a/backend/internal/repository/clickhouse_repo.go b/backend/internal/repository/clickhouse_repo.go index 4b3fe03d..e688c10d 100644 --- a/backend/internal/repository/clickhouse_repo.go +++ b/backend/internal/repository/clickhouse_repo.go @@ -1,12 +1,11 @@ package repository import ( + "baron-sso-backend/internal/domain" "context" "fmt" "time" - "baron-sso-backend/internal/domain" - "github.com/ClickHouse/clickhouse-go/v2" "github.com/ClickHouse/clickhouse-go/v2/lib/driver" ) @@ -25,7 +24,6 @@ func NewClickHouseRepository(host string, port int, user, password, db string) ( }, Debug: false, }) - if err != nil { return nil, fmt.Errorf("failed to open clickhouse connection: %w", err) } diff --git a/backend/internal/service/ses_service.go b/backend/internal/service/ses_service.go index f38b37f2..598ad517 100644 --- a/backend/internal/service/ses_service.go +++ b/backend/internal/service/ses_service.go @@ -1,12 +1,12 @@ package service import ( + "baron-sso-backend/internal/domain" "context" "fmt" "log/slog" "os" - "baron-sso-backend/internal/domain" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" diff --git a/backend/internal/service/sms_service.go b/backend/internal/service/sms_service.go index 8ebc6059..c50a739a 100644 --- a/backend/internal/service/sms_service.go +++ b/backend/internal/service/sms_service.go @@ -1,6 +1,7 @@ package service import ( + "baron-sso-backend/internal/domain" "bytes" "crypto/hmac" "crypto/sha256" @@ -14,8 +15,6 @@ import ( "strconv" "strings" "time" - - "baron-sso-backend/internal/domain" ) type SmsServiceImpl struct { @@ -96,8 +95,8 @@ func (s *SmsServiceImpl) SendSms(to, content string) error { slog.Error("[SmsService] error response from naver cloud sms api", "body", string(respBody)) return fmt.Errorf("error sending sms: status code %d", resp.StatusCode) } - - slog.Info("[SmsService] sms sent successfully", "body", string(respBody)) + + slog.Info("[SmsService] sms sent successfully", "body", string(respBody)) return nil } @@ -113,4 +112,4 @@ func (s *SmsServiceImpl) makeSignature(method, url, timestamp string) (string, e } return base64.StdEncoding.EncodeToString(h.Sum(nil)), nil -} \ No newline at end of file +}