1
0
forked from baron/baron-sso

aws ses 구현

This commit is contained in:
2026-01-19 17:04:48 +09:00
parent 27b8ff2ac1
commit 3c41c0dc62
8 changed files with 268 additions and 188 deletions

View File

@@ -0,0 +1,6 @@
package domain
// EmailService defines the interface for sending emails.
type EmailService interface {
SendEmail(to, subject, body string) error
}

View File

@@ -35,6 +35,7 @@ const (
type AuthHandler struct {
ProjectID string
SmsService domain.SmsService
EmailService domain.EmailService
RedisService *service.RedisService
DescopeClient *client.DescopeClient
}
@@ -71,6 +72,7 @@ func NewAuthHandler() *AuthHandler {
return &AuthHandler{
ProjectID: projectID,
SmsService: service.NewSmsService(),
EmailService: service.NewEmailService(),
RedisService: redisService,
DescopeClient: descopeClient,
}
@@ -143,24 +145,52 @@ func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
h.RedisService.Set(prefixSession+pendingRef, fmt.Sprintf(`{"status":"%s"}`, statusPending), defaultExpiration)
h.RedisService.Set(prefixToken+token, fmt.Sprintf(`{"pendingRef":"%s","loginId":"%s"}`, pendingRef, loginID), defaultExpiration)
// Send SMS
// Generate Link
frontendURL := os.Getenv("FRONTEND_URL")
if frontendURL == "" {
frontendURL = "http://ssologin.hmac.kr"
}
link := fmt.Sprintf("%s/verify/%s", frontendURL, token)
content := fmt.Sprintf("[Baron SSO] 로그인 링크: %s", link)
log.Printf("[Enchanted] Sending SMS to %s via Naver Cloud", loginID)
// Route based on LoginID type
if strings.Contains(loginID, "@") {
// Send Email
if h.EmailService == nil {
log.Printf("[Enchanted] Email Service not configured")
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Email service not configured"})
}
if err := h.SmsService.SendSms(loginID, content); err != nil {
log.Printf("[Enchanted] SMS Failed: %v", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send SMS"})
subject := "[Baron SSO] 로그인 링크"
body := fmt.Sprintf(`
<div style="font-family: sans-serif; padding: 20px; border: 1px solid #eee; border-radius: 10px; max-width: 500px;">
<h2 style="color: #1A1F2C;">Baron SSO 로그인</h2>
<p>안녕하세요,</p>
<p>아래 버튼을 클릭하여 로그인을 완료해 주세요. 이 링크는 5분 동안 유효합니다.</p>
<div style="margin: 30px 0;">
<a href="%s" style="background-color: #1A1F2C; color: white; padding: 12px 24px; text-decoration: none; border-radius: 5px; font-weight: bold;">로그인 완료하기</a>
</div>
<p style="font-size: 12px; color: #888;">만약 본인이 요청하지 않았다면 이 메일을 무시하셔도 됩니다.</p>
</div>
`, link)
log.Printf("[Enchanted] Sending Email to %s via AWS SES", loginID)
if err := h.EmailService.SendEmail(loginID, subject, body); err != nil {
log.Printf("[Enchanted] Email Failed: %v", 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)
if err := h.SmsService.SendSms(loginID, content); err != nil {
log.Printf("[Enchanted] SMS Failed: %v", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send SMS"})
}
}
log.Printf("[Enchanted] SMS sent successfully to %s", loginID)
return c.JSON(fiber.Map{
"linkId": "SMS Sent",
"linkId": "Sent",
"pendingRef": pendingRef,
"maskedEmail": loginID,
})

View File

@@ -0,0 +1,79 @@
package service
import (
"context"
"fmt"
"log"
"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"
"github.com/aws/aws-sdk-go-v2/service/ses"
"github.com/aws/aws-sdk-go-v2/service/ses/types"
)
type SesServiceImpl struct {
client *ses.Client
sender string
}
func NewEmailService() domain.EmailService {
region := os.Getenv("AWS_REGION")
accessKey := os.Getenv("AWS_ACCESS_KEY_ID")
secretKey := os.Getenv("AWS_SECRET_ACCESS_KEY")
sender := os.Getenv("AWS_SES_SENDER")
if region == "" || accessKey == "" || secretKey == "" {
log.Println("[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)
return nil
}
return &SesServiceImpl{
client: ses.NewFromConfig(cfg),
sender: sender,
}
}
func (s *SesServiceImpl) SendEmail(to, subject, body string) error {
if s == nil || s.client == nil {
return fmt.Errorf("email service not initialized")
}
input := &ses.SendEmailInput{
Destination: &types.Destination{
ToAddresses: []string{to},
},
Message: &types.Message{
Body: &types.Body{
Html: &types.Content{
Charset: aws.String("UTF-8"),
Data: aws.String(body),
},
},
Subject: &types.Content{
Charset: aws.String("UTF-8"),
Data: aws.String(subject),
},
},
Source: aws.String(s.sender),
}
_, err := s.client.SendEmail(context.TODO(), input)
if err != nil {
log.Printf("[EmailService] Failed to send email to %s: %v", to, err)
} else {
log.Printf("[EmailService] Email sent successfully to %s", to)
}
return err
}