forked from baron/baron-sso
slog 통합
This commit is contained in:
@@ -1,27 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"baron-sso-backend/internal/handler"
|
||||
"baron-sso-backend/internal/logger"
|
||||
"baron-sso-backend/internal/repository"
|
||||
"baron-sso-backend/internal/service"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/snowflake"
|
||||
|
||||
"baron-sso-backend/internal/handler"
|
||||
|
||||
"baron-sso-backend/internal/logger"
|
||||
|
||||
"baron-sso-backend/internal/repository"
|
||||
|
||||
"baron-sso-backend/internal/service"
|
||||
|
||||
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
|
||||
|
||||
"github.com/bwmarrin/snowflake"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/gofiber/fiber/v2/middleware/encryptcookie"
|
||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||
@@ -41,7 +33,6 @@ func main() {
|
||||
ServiceName: "baron-sso",
|
||||
Environment: getEnv("GO_ENV", "dev"),
|
||||
})
|
||||
|
||||
// Initialize Snowflake Node (Node 2 for Baron)
|
||||
node, err := snowflake.NewNode(2)
|
||||
if err != nil {
|
||||
@@ -50,10 +41,22 @@ func main() {
|
||||
}
|
||||
|
||||
// 1. Log Config on Startup
|
||||
slog.Info("Starting Baron SSO Backend",
|
||||
fmt.Println("============================================================")
|
||||
fmt.Println(`
|
||||
|\__/,| (\
|
||||
_.|o o |_ ) )
|
||||
-(((---(((--------
|
||||
`)
|
||||
fmt.Println("🚀 Baron SSO Backend Starting...")
|
||||
|
||||
slog.Info("Service starting",
|
||||
"service", "baron-sso",
|
||||
"app_env", getEnv("APP_ENV", "dev"),
|
||||
"db_port", getEnv("DB_PORT", "5532"),
|
||||
"backend_port", getEnv("BACKEND_PORT", "3000"),
|
||||
"frontend_port", getEnv("FRONTEND_PORT", "5000"),
|
||||
"frontend_url", getEnv("FRONTEND_URL", "http://ssologin.hmac.kr"),
|
||||
"redis_addr", getEnv("REDIS_ADDR", "redis:6379"),
|
||||
"descope_id", getEnv("DESCOPE_PROJECT_ID", "not-set"),
|
||||
)
|
||||
|
||||
// 2. Initialize DB Connections
|
||||
@@ -80,7 +83,7 @@ func main() {
|
||||
|
||||
// 3. Initialize Fiber
|
||||
app := fiber.New(fiber.Config{
|
||||
AppName: "Baron SSO Backend",
|
||||
AppName: "Baron SSO Backend",
|
||||
DisableStartupMessage: true, // Clean logs
|
||||
})
|
||||
|
||||
@@ -94,10 +97,10 @@ func main() {
|
||||
// [Standardized] HTTP Request Logger Middleware using slog
|
||||
app.Use(func(c *fiber.Ctx) error {
|
||||
start := time.Now()
|
||||
|
||||
|
||||
// Handle request
|
||||
err := c.Next()
|
||||
|
||||
|
||||
// Log after request
|
||||
latency := time.Since(start)
|
||||
status := c.Response().StatusCode()
|
||||
@@ -107,7 +110,7 @@ func main() {
|
||||
if status < 400 {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
msg := "http_request"
|
||||
if err != nil {
|
||||
msg = "http_request_error"
|
||||
@@ -129,7 +132,7 @@ func main() {
|
||||
AllowOrigins: "*", // Adjust in production
|
||||
AllowHeaders: "Origin, Content-Type, Accept, Authorization",
|
||||
AllowMethods: "GET, POST, HEAD, PUT, DELETE, PATCH, OPTIONS",
|
||||
}))
|
||||
}))
|
||||
app.Use(encryptcookie.New(encryptcookie.Config{
|
||||
Key: getEnv("COOKIE_SECRET", "secret-key-must-be-32-bytes-long!"),
|
||||
}))
|
||||
@@ -185,7 +188,7 @@ func main() {
|
||||
// API Group
|
||||
api := app.Group("/api/v1")
|
||||
api.Post("/audit", auditHandler.CreateLog)
|
||||
|
||||
|
||||
// Auth Proxy Routes
|
||||
auth := api.Group("/auth")
|
||||
auth.Post("/enchanted-link/init", authHandler.InitEnchantedLink)
|
||||
@@ -196,7 +199,7 @@ func main() {
|
||||
auth.Post("/qr/init", authHandler.InitQRLogin)
|
||||
auth.Post("/qr/poll", authHandler.PollQRLogin)
|
||||
auth.Post("/qr/approve", authHandler.ScanQRLogin)
|
||||
|
||||
|
||||
// Admin Routes
|
||||
admin := api.Group("/admin")
|
||||
admin.Post("/users", adminHandler.CreateUser)
|
||||
@@ -205,10 +208,10 @@ func main() {
|
||||
admin.Patch("/users/:loginId", adminHandler.UpdateUser)
|
||||
admin.Delete("/users/:loginId", adminHandler.DeleteUser)
|
||||
admin.Patch("/users/:loginId/status", adminHandler.UpdateUserStatus)
|
||||
|
||||
|
||||
// Webhook for Descope Generic SMS Gateway
|
||||
auth.Post("/webhooks/descope-sms", authHandler.HandleDescopeSmsRelay)
|
||||
|
||||
|
||||
// Webhook for Descope Generic Email Gateway (Fake Email Strategy)
|
||||
auth.Post("/webhooks/descope-email", authHandler.HandleDescopeEmailRelay)
|
||||
|
||||
@@ -229,7 +232,7 @@ func main() {
|
||||
slog.String("source", "client"),
|
||||
}
|
||||
for k, v := range req.Data {
|
||||
// Skip svc if it's already set by the global logger to avoid confusion,
|
||||
// Skip svc if it's already set by the global logger to avoid confusion,
|
||||
// or keep it as client_svc
|
||||
if k == "svc" {
|
||||
attrs = append(attrs, slog.Any("client_svc", v))
|
||||
@@ -252,9 +255,10 @@ func main() {
|
||||
// Filter out noisy client navigation logs
|
||||
if level == slog.LevelInfo {
|
||||
msg := strings.ToLower(req.Message)
|
||||
if strings.Contains(msg, "navigating to") ||
|
||||
strings.Contains(msg, "going to") ||
|
||||
strings.Contains(msg, "redirecting to") {
|
||||
if strings.Contains(msg, "navigating to") ||
|
||||
strings.Contains(msg, "going to") ||
|
||||
strings.Contains(msg, "redirecting to") ||
|
||||
strings.Contains(msg, "full paths for routes") {
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
}
|
||||
@@ -264,8 +268,9 @@ func main() {
|
||||
})
|
||||
|
||||
// Start Server
|
||||
port := getEnv("PORT", "3000")
|
||||
port := getEnv("BACKEND_PORT", "3000")
|
||||
slog.Info("Server listening", "port", port)
|
||||
fmt.Println("============================================================")
|
||||
if err := app.Listen(":" + port); err != nil {
|
||||
slog.Error("Server failed to start", "error", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -2,7 +2,7 @@ package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
"net/url"
|
||||
@@ -29,10 +29,10 @@ func NewAdminHandler() *AdminHandler {
|
||||
ManagementKey: managementKey,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Warning: Failed to initialize Descope Client for Admin: %v", err)
|
||||
slog.Warn("Failed to initialize Descope Client for Admin", "error", err)
|
||||
}
|
||||
} else {
|
||||
log.Println("Warning: DESCOPE_PROJECT_ID or DESCOPE_MANAGEMENT_KEY missing. Admin functions will fail.")
|
||||
slog.Warn("DESCOPE_PROJECT_ID or DESCOPE_MANAGEMENT_KEY missing. Admin functions will fail.")
|
||||
}
|
||||
|
||||
return &AdminHandler{
|
||||
@@ -95,7 +95,7 @@ func (h *AdminHandler) ListUsers(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("[Admin] ListUsers failed: %v", err)
|
||||
slog.Error("[Admin] ListUsers failed", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
@@ -112,9 +112,9 @@ func (h *AdminHandler) DeleteUser(c *fiber.Ctx) error {
|
||||
loginID = decoded
|
||||
}
|
||||
|
||||
log.Printf("[Admin] Deleting user: %s", loginID)
|
||||
slog.Info("[Admin] Deleting user", "loginID", loginID)
|
||||
if err := h.DescopeClient.Management.User().Delete(context.Background(), loginID); err != nil {
|
||||
log.Printf("[Admin] DeleteUser failed: %v", err)
|
||||
slog.Error("[Admin] DeleteUser failed", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ func (h *AdminHandler) UpdateUserStatus(c *fiber.Ctx) error {
|
||||
var user *descope.UserResponse
|
||||
var err error
|
||||
|
||||
log.Printf("[Admin] Updating status for %s to %s", loginID, req.Status)
|
||||
slog.Info("[Admin] Updating status", "loginID", loginID, "status", req.Status)
|
||||
|
||||
if req.Status == "enabled" || req.Status == "active" {
|
||||
user, err = h.DescopeClient.Management.User().Activate(context.Background(), loginID)
|
||||
@@ -149,7 +149,7 @@ func (h *AdminHandler) UpdateUserStatus(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("[Admin] Status update failed: %v", err)
|
||||
slog.Error("[Admin] Status update failed", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
@@ -266,11 +266,11 @@ func (h *AdminHandler) CreateUser(c *fiber.Ctx) error {
|
||||
userObj.Tenants = userTenants
|
||||
}
|
||||
|
||||
log.Printf("[Admin] Creating user: %s (Email: %s, Phone: %s)", req.LoginID, req.Email, normalizedPhone)
|
||||
slog.Info("[Admin] Creating user", "loginID", req.LoginID, "email", req.Email, "phone", normalizedPhone)
|
||||
|
||||
res, err := h.DescopeClient.Management.User().Create(context.Background(), req.LoginID, userObj)
|
||||
if err != nil {
|
||||
log.Printf("[Admin] Failed to create user: %v", err)
|
||||
slog.Error("[Admin] Failed to create user", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
|
||||
@@ -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"})
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"baron-sso-backend/internal/domain"
|
||||
@@ -26,16 +26,15 @@ func NewEmailService() domain.EmailService {
|
||||
sender := os.Getenv("AWS_SES_SENDER")
|
||||
|
||||
if region == "" || accessKey == "" || secretKey == "" {
|
||||
log.Println("[EmailService] AWS configuration missing, email service will not work")
|
||||
slog.Warn("[EmailService] AWS configuration missing, email service will not work")
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg, err := config.LoadDefaultConfig(context.TODO(),
|
||||
config.WithRegion(region),
|
||||
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("[EmailService] Failed to load AWS config: %v", err)
|
||||
slog.Error("Failed to load AWS config", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -71,9 +70,9 @@ func (s *SesServiceImpl) SendEmail(to, subject, body string) error {
|
||||
|
||||
_, err := s.client.SendEmail(context.TODO(), input)
|
||||
if err != nil {
|
||||
log.Printf("[EmailService] Failed to send email to %s: %v", to, err)
|
||||
slog.Error("[EmailService] Failed to send email", "to", to, "error", err)
|
||||
} else {
|
||||
log.Printf("[EmailService] Email sent successfully to %s", to)
|
||||
slog.Info("[EmailService] Email sent successfully", "to", to)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
"baron-sso-backend/internal/domain"
|
||||
)
|
||||
|
||||
type SmsServiceImpl struct {
|
||||
accessKey string
|
||||
secretKey string
|
||||
@@ -28,7 +29,7 @@ func NewSmsService() domain.SmsService {
|
||||
// Sanitize sender phone number right after reading from env
|
||||
rawSenderPhone := os.Getenv("NAVER_SENDER_PHONE_NUMBER")
|
||||
sanitizedSenderPhone := strings.ReplaceAll(rawSenderPhone, "-", "")
|
||||
log.Printf("[서비스 초기화] 발신자 번호 처리: 원본='%s', 정제 후='%s'", rawSenderPhone, sanitizedSenderPhone)
|
||||
slog.Info("[서비스 초기화] 발신자 번호 처리", "원본", rawSenderPhone, "정제후", sanitizedSenderPhone)
|
||||
|
||||
return &SmsServiceImpl{
|
||||
accessKey: os.Getenv("NAVER_CLOUD_ACCESS_KEY"),
|
||||
@@ -41,7 +42,7 @@ func NewSmsService() domain.SmsService {
|
||||
func (s *SmsServiceImpl) SendSms(to, content string) error {
|
||||
timestamp := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)
|
||||
apiURL := fmt.Sprintf("https://sens.apigw.ntruss.com/sms/v2/services/%s/messages", s.serviceID)
|
||||
log.Printf("Requesting SENS API URL: %s", apiURL)
|
||||
slog.Info("[SmsService] Requesting SENS API URL", "url", apiURL)
|
||||
|
||||
// Naver SENS API requires phone number without '+'
|
||||
sanitizedTo := strings.Replace(to, "+", "", 1)
|
||||
@@ -92,11 +93,11 @@ func (s *SmsServiceImpl) SendSms(to, content string) error {
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 300 {
|
||||
log.Printf("error response from naver cloud sms api: %s", string(respBody))
|
||||
slog.Error("[SmsService] error response from naver cloud sms api", "body", string(respBody))
|
||||
return fmt.Errorf("error sending sms: status code %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
log.Printf("sms sent successfully: %s", string(respBody))
|
||||
slog.Info("[SmsService] sms sent successfully", "body", string(respBody))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -112,4 +113,4 @@ func (s *SmsServiceImpl) makeSignature(method, url, timestamp string) (string, e
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user