forked from baron/baron-sso
slog 통합
This commit is contained in:
@@ -8,7 +8,7 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -55,35 +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 {
|
||||
log.Printf("Warning: Failed to initialize Descope Client: %v", err)
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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"})
|
||||
}
|
||||
|
||||
log.Printf("[SMS] Sending code to: %s", req.PhoneNumber)
|
||||
sanitizedPhone := strings.ReplaceAll(req.PhoneNumber, "-", "")
|
||||
|
||||
|
||||
// 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)
|
||||
@@ -124,7 +123,7 @@ func (h *AuthHandler) VerifySms(c *fiber.Ctx) error {
|
||||
func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
|
||||
var req domain.EnchantedLinkInitRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
log.Printf("[Enchanted] Body parse error: %v", err)
|
||||
slog.Error("[Enchanted] Body parse error", "error", err)
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
|
||||
}
|
||||
|
||||
@@ -135,7 +134,7 @@ func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
|
||||
token := GenerateSecureToken(3)
|
||||
pendingRef := GenerateSecureToken(3)
|
||||
|
||||
log.Printf("[Enchanted] Initiating for %s. Token: %s, PendingRef: %s", loginID, token, pendingRef)
|
||||
slog.Info("[Enchanted] Initiating enchanted link", "loginID", loginID, "token", token, "pendingRef", pendingRef)
|
||||
|
||||
// Store in Redis
|
||||
h.RedisService.Set(prefixSession+pendingRef, fmt.Sprintf(`{"status":"%s"}`, statusPending), defaultExpiration)
|
||||
@@ -152,7 +151,7 @@ func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
|
||||
if strings.Contains(loginID, "@") {
|
||||
// Send Email
|
||||
if h.EmailService == nil {
|
||||
log.Printf("[Enchanted] Email Service not configured")
|
||||
slog.Error("[Enchanted] Email Service not configured")
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Email service not configured"})
|
||||
}
|
||||
|
||||
@@ -169,18 +168,18 @@ func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
|
||||
</div>
|
||||
`, link)
|
||||
|
||||
log.Printf("[Enchanted] Sending Email to %s via AWS SES", loginID)
|
||||
slog.Info("[Enchanted] Sending Email via AWS SES", "loginID", loginID)
|
||||
if err := h.EmailService.SendEmail(loginID, subject, body); err != nil {
|
||||
log.Printf("[Enchanted] Email Failed: %v", err)
|
||||
slog.Error("[Enchanted] Email Failed", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send Email"})
|
||||
}
|
||||
} else {
|
||||
// Send SMS
|
||||
content := fmt.Sprintf("[Baron SSO] 로그인 링크: %s", link)
|
||||
log.Printf("[Enchanted] Sending SMS to %s via Naver Cloud", loginID)
|
||||
slog.Info("[Enchanted] Sending SMS via Naver Cloud", "loginID", loginID)
|
||||
|
||||
if err := h.SmsService.SendSms(loginID, content); err != nil {
|
||||
log.Printf("[Enchanted] SMS Failed: %v", err)
|
||||
slog.Error("[Enchanted] SMS Failed", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send SMS"})
|
||||
}
|
||||
}
|
||||
@@ -208,7 +207,7 @@ func (h *AuthHandler) PollEnchantedLink(c *fiber.Ctx) error {
|
||||
json.Unmarshal([]byte(val), &data)
|
||||
|
||||
if data["status"] == statusSuccess {
|
||||
log.Printf("[Poll] Success for ref: %s", req.PendingRef)
|
||||
slog.Info("[Poll] Success", "pendingRef", req.PendingRef)
|
||||
return c.JSON(fiber.Map{
|
||||
"sessionJwt": data["jwt"],
|
||||
"status": "ok",
|
||||
@@ -222,16 +221,16 @@ func (h *AuthHandler) PollEnchantedLink(c *fiber.Ctx) error {
|
||||
func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
|
||||
var req domain.MagicLinkVerifyRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
log.Printf("[Verify] Body parse error: %v", err)
|
||||
slog.Error("[Verify] Body parse error", "error", err)
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
|
||||
}
|
||||
|
||||
log.Printf("[Verify] Attempting to verify token: %s", req.Token)
|
||||
slog.Info("[Verify] Attempting to verify token", "token", req.Token)
|
||||
|
||||
tokenKey := prefixToken + req.Token
|
||||
val, err := h.RedisService.Get(tokenKey)
|
||||
if err != nil || val == "" {
|
||||
log.Printf("[Verify] Token not found or expired in Redis: %s", req.Token)
|
||||
slog.Warn("[Verify] Token not found or expired in Redis", "token", req.Token)
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid or expired token"})
|
||||
}
|
||||
|
||||
@@ -240,11 +239,11 @@ func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
|
||||
pendingRef := tokenData["pendingRef"]
|
||||
loginID := tokenData["loginId"]
|
||||
|
||||
log.Printf("[Verify] Token valid. LoginID: %s, PendingRef: %s", loginID, pendingRef)
|
||||
slog.Info("[Verify] Token valid", "loginID", loginID, "pendingRef", pendingRef)
|
||||
|
||||
// 1. Generate Descope Session Directly (Management SDK)
|
||||
if h.DescopeClient == nil {
|
||||
log.Printf("[Verify] Descope Client is nil!")
|
||||
slog.Error("[Verify] Descope Client is nil!")
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Descope Client not configured"})
|
||||
}
|
||||
|
||||
@@ -260,7 +259,7 @@ func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[Verify] Searching for user with phone: %s", searchPhone)
|
||||
slog.Info("[Verify] Searching for user", "phone", searchPhone)
|
||||
searchOptions := &descope.UserSearchOptions{
|
||||
Phones: []string{searchPhone},
|
||||
Limit: 1,
|
||||
@@ -272,24 +271,24 @@ func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
|
||||
if errSearch == nil && len(users) > 0 {
|
||||
if len(users[0].LoginIDs) > 0 {
|
||||
targetLoginID = users[0].LoginIDs[0]
|
||||
log.Printf("[Verify] User found! Existing LoginID: %s", targetLoginID)
|
||||
slog.Info("[Verify] User found", "existingLoginID", targetLoginID)
|
||||
} else {
|
||||
// Should not happen for a valid user, but fallback to UserID or searchPhone
|
||||
log.Printf("[Verify] User found but no LoginIDs. Using UserID.")
|
||||
slog.Warn("[Verify] User found but no LoginIDs, using UserID")
|
||||
targetLoginID = users[0].UserID
|
||||
}
|
||||
} else {
|
||||
// Not found, or search error. Fallback to using the phone as LoginID.
|
||||
// Use the normalized phone number to ensure consistency (+82...)
|
||||
targetLoginID = searchPhone
|
||||
log.Printf("[Verify] User not found by phone. Will use/create: %s", targetLoginID)
|
||||
slog.Info("[Verify] User not found by phone, will use/create", "loginID", targetLoginID)
|
||||
}
|
||||
|
||||
log.Printf("[Verify] Generating embedded link for %s", targetLoginID)
|
||||
slog.Info("[Verify] Generating embedded link", "loginID", targetLoginID)
|
||||
embeddedToken, err := h.DescopeClient.Management.User().GenerateEmbeddedLink(context.Background(), targetLoginID, nil, 0)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "User not found") || strings.Contains(err.Error(), "E062108") {
|
||||
log.Printf("[Verify] User %s not found. Creating...", targetLoginID)
|
||||
slog.Info("[Verify] User not found, creating...", "loginID", targetLoginID)
|
||||
|
||||
// Create User with Explicit Phone Attribute
|
||||
userObj := &descope.UserRequest{}
|
||||
@@ -301,30 +300,30 @@ func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
|
||||
|
||||
_, errCreate := h.DescopeClient.Management.User().Create(context.Background(), targetLoginID, userObj)
|
||||
if errCreate != nil {
|
||||
log.Printf("[Verify] Failed to create user: %v", errCreate)
|
||||
slog.Error("[Verify] Failed to create user", "error", errCreate)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to create new user"})
|
||||
}
|
||||
|
||||
embeddedToken, err = h.DescopeClient.Management.User().GenerateEmbeddedLink(context.Background(), targetLoginID, nil, 0)
|
||||
if err != nil {
|
||||
log.Printf("[Verify] Failed to generate token after creation: %v", err)
|
||||
slog.Error("[Verify] Failed to generate token after creation", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to generate upstream token"})
|
||||
}
|
||||
} else {
|
||||
log.Printf("[Verify] Descope Error: %v", err)
|
||||
slog.Error("[Verify] Descope Error", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to generate upstream token"})
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[Verify] Exchanging embedded token for session JWT")
|
||||
slog.Info("[Verify] Exchanging embedded token for session JWT")
|
||||
authInfo, err := h.DescopeClient.Auth.MagicLink().Verify(context.Background(), embeddedToken, nil)
|
||||
if err != nil {
|
||||
log.Printf("[Verify] Final verification failed: %v", err)
|
||||
slog.Error("[Verify] Final verification failed", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to verify upstream token"})
|
||||
}
|
||||
sessionToken := authInfo.SessionToken.JWT
|
||||
|
||||
log.Printf("[Verify] Success! Updating Redis session: %s", pendingRef)
|
||||
slog.Info("[Verify] Success! Updating Redis session", "pendingRef", pendingRef)
|
||||
sessionData, _ := json.Marshal(map[string]string{
|
||||
"status": statusSuccess,
|
||||
"jwt": sessionToken,
|
||||
@@ -348,7 +347,7 @@ func (h *AuthHandler) InitQRLogin(c *fiber.Ctx) error {
|
||||
}
|
||||
qrPayload := fmt.Sprintf("%s/approve?ref=%s", frontendURL, pendingRef)
|
||||
|
||||
log.Printf("[QR] Init: PendingRef=%s, URL=%s", pendingRef, qrPayload)
|
||||
slog.Info("[QR] Init", "pendingRef", pendingRef, "url", qrPayload)
|
||||
|
||||
// Redis에 초기 상태 저장 (5분 만료)
|
||||
h.RedisService.Set(prefixSession+pendingRef, fmt.Sprintf(`{"status":"%s"}`, statusPending), 5*time.Minute)
|
||||
@@ -378,6 +377,7 @@ func (h *AuthHandler) PollQRLogin(c *fiber.Ctx) error {
|
||||
json.Unmarshal([]byte(val), &data)
|
||||
|
||||
if data["status"] == statusSuccess {
|
||||
slog.Info("[QR] Poll Success", "pendingRef", req.PendingRef)
|
||||
return c.JSON(fiber.Map{
|
||||
"status": "ok",
|
||||
"sessionJwt": data["jwt"],
|
||||
@@ -395,10 +395,11 @@ func (h *AuthHandler) ScanQRLogin(c *fiber.Ctx) error {
|
||||
Token string `json:"token"` // 모바일 사용자의 세션 토큰 (검증용)
|
||||
}
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
slog.Error("[QR] Scan body parse error", "error", err)
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid body"})
|
||||
}
|
||||
|
||||
log.Printf("[QR] Scan & Approve: PendingRef=%s", req.PendingRef)
|
||||
slog.Info("[QR] Scan & Approve", "pendingRef", req.PendingRef)
|
||||
|
||||
// 1. Redis에서 세션 확인
|
||||
val, err := h.RedisService.Get(prefixSession + req.PendingRef)
|
||||
@@ -430,16 +431,16 @@ func (h *AuthHandler) HandleDescopeSmsRelay(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
log.Printf("[Webhook] Body parsing failed: %v", err)
|
||||
slog.Error("Webhook Body parsing failed", "error", err)
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
|
||||
}
|
||||
|
||||
if req.Recipient == "" || req.Body == "" {
|
||||
log.Printf("[Webhook] Missing recipient or body")
|
||||
slog.Warn("Webhook missing recipient or body")
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Missing recipient or body"})
|
||||
}
|
||||
|
||||
log.Printf("[Webhook] Received SMS request for %s", req.Recipient)
|
||||
slog.Info("Received SMS request", "recipient", req.Recipient)
|
||||
|
||||
phone := req.Recipient
|
||||
if strings.HasPrefix(phone, "+82") {
|
||||
@@ -449,11 +450,11 @@ func (h *AuthHandler) HandleDescopeSmsRelay(c *fiber.Ctx) error {
|
||||
phone = strings.ReplaceAll(phone, " ", "")
|
||||
|
||||
if err := h.SmsService.SendSms(phone, req.Body); err != nil {
|
||||
log.Printf("[Webhook] Failed to forward SMS to Naver: %v", err)
|
||||
slog.Error("Failed to forward SMS to Naver", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send SMS via Naver"})
|
||||
}
|
||||
|
||||
log.Printf("[Webhook] Successfully forwarded SMS to %s", phone)
|
||||
slog.Info("Successfully forwarded SMS", "phone", phone)
|
||||
return c.JSON(fiber.Map{"status": "ok"})
|
||||
}
|
||||
|
||||
@@ -467,11 +468,11 @@ func (h *AuthHandler) HandleDescopeEmailRelay(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
log.Printf("[Email Webhook] Body parsing failed: %v", err)
|
||||
slog.Error("[Email Webhook] Body parsing failed", "error", err)
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
|
||||
}
|
||||
|
||||
log.Printf("[Email Webhook] Received email request for %s", req.To)
|
||||
slog.Info("[Email Webhook] Received email request", "to", req.To)
|
||||
|
||||
// Check if it's a Fake Email for SMS
|
||||
if strings.HasSuffix(req.To, "@sms.baron") {
|
||||
@@ -484,16 +485,16 @@ func (h *AuthHandler) HandleDescopeEmailRelay(c *fiber.Ctx) error {
|
||||
|
||||
// Send SMS with the text body (Descope template should be optimized for SMS)
|
||||
if err := h.SmsService.SendSms(phone, req.Text); err != nil {
|
||||
log.Printf("[Email Webhook] Failed to forward Email-as-SMS: %v", err)
|
||||
slog.Error("[Email Webhook] Failed to forward Email-as-SMS", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send SMS"})
|
||||
}
|
||||
|
||||
log.Printf("[Email Webhook] Successfully converted Email to SMS for %s", phone)
|
||||
slog.Info("[Email Webhook] Successfully converted Email to SMS", "phone", phone)
|
||||
return c.JSON(fiber.Map{"status": "ok"})
|
||||
}
|
||||
|
||||
// Real Email Handling (Not implemented in this Relay)
|
||||
// You would need an SMTP service here if you route ALL emails through this relay.
|
||||
log.Printf("[Email Webhook] Real email skipped (Not implemented): %s", req.To)
|
||||
slog.Warn("[Email Webhook] Real email skipped (Not implemented)", "to", req.To)
|
||||
return c.Status(501).JSON(fiber.Map{"error": "Real email sending not implemented"})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user