forked from baron/baron-sso
링크로그인 동작
This commit is contained in:
@@ -28,16 +28,16 @@ import (
|
||||
|
||||
const (
|
||||
// Redis Key Prefixes
|
||||
prefixSession = "enchanted_session:"
|
||||
prefixToken = "enchanted_token:"
|
||||
prefixLoginCode = "login_code_flow:"
|
||||
prefixLoginCodePending = "login_code_pending:"
|
||||
prefixSession = "enchanted_session:"
|
||||
prefixToken = "enchanted_token:"
|
||||
prefixLoginCode = "login_code_flow:"
|
||||
prefixLoginCodePending = "login_code_pending:"
|
||||
prefixLoginCodeSmsTarget = "login_code_sms_target:"
|
||||
prefixLoginCodeSmsLookup = "login_code_sms_lookup:"
|
||||
prefixLoginCodeShort = "login_code_short:"
|
||||
prefixPollMeta = "poll_meta:"
|
||||
prefixSignupEmail = "signup:email:"
|
||||
prefixSignupPhone = "signup:phone:"
|
||||
prefixLoginCodeShort = "login_code_short:"
|
||||
prefixPollMeta = "poll_meta:"
|
||||
prefixSignupEmail = "signup:email:"
|
||||
prefixSignupPhone = "signup:phone:"
|
||||
|
||||
// Session Statuses
|
||||
statusPending = "pending"
|
||||
@@ -54,6 +54,7 @@ const (
|
||||
pwdResetExpiration = 15 * time.Minute
|
||||
minPollInterval = 2 * time.Second
|
||||
loginCodeExpiration = 10 * time.Minute
|
||||
linkResendCooldown = 60 * time.Second
|
||||
)
|
||||
|
||||
type AuthHandler struct {
|
||||
@@ -651,6 +652,7 @@ func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
|
||||
"provider": h.IdpProvider.Name(),
|
||||
"expiresIn": expiresIn,
|
||||
"interval": int(minPollInterval.Seconds()),
|
||||
"resendAfter": int(linkResendCooldown.Seconds()),
|
||||
})
|
||||
} else if err != nil && !errors.Is(err, domain.ErrNotSupported) {
|
||||
slog.Error("[Enchanted] Link login init failed", "provider", h.IdpProvider.Name(), "error", err)
|
||||
@@ -716,6 +718,7 @@ func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
|
||||
"maskedEmail": loginID,
|
||||
"expiresIn": int(defaultExpiration.Seconds()),
|
||||
"interval": int(minPollInterval.Seconds()),
|
||||
"resendAfter": int(linkResendCooldown.Seconds()),
|
||||
"userCode": userCode,
|
||||
})
|
||||
}
|
||||
@@ -1490,6 +1493,10 @@ func (h *AuthHandler) HandleKratosCourierRelay(c *fiber.Ctx) error {
|
||||
if h.EmailService == nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Email service not configured"})
|
||||
}
|
||||
if shortSubject, shortBody := h.buildKratosShortEmailBody(&req, req.Recipient); shortBody != "" {
|
||||
subject = shortSubject
|
||||
body = shortBody
|
||||
}
|
||||
if err := h.EmailService.SendEmail(req.Recipient, subject, body); err != nil {
|
||||
slog.Error("[Kratos Courier] Email send failed", "to", req.Recipient, "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send email"})
|
||||
@@ -1602,16 +1609,44 @@ type shortLoginCodePayload struct {
|
||||
}
|
||||
|
||||
func (h *AuthHandler) buildKratosShortSmsBody(req *kratosCourierRequest, loginID, phone string) string {
|
||||
if req == nil || loginID == "" {
|
||||
_, link, ok := h.prepareKratosShortLogin(req, loginID)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("[Baron 통합로그인] %s", link)
|
||||
}
|
||||
|
||||
func (h *AuthHandler) buildKratosShortEmailBody(req *kratosCourierRequest, loginID string) (string, string) {
|
||||
shortCode, link, ok := h.prepareKratosShortLogin(req, loginID)
|
||||
if !ok {
|
||||
return "", ""
|
||||
}
|
||||
subject := "[Baron 통합로그인] 로그인 링크"
|
||||
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>
|
||||
<div style="margin: 24px 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: 14px;">간편 코드: <strong>%s</strong></p>
|
||||
<p style="font-size: 12px; color: #888;">링크가 열리지 않으면 위 간편 코드를 입력해 로그인할 수 있습니다.</p>
|
||||
</div>
|
||||
`, link, shortCode)
|
||||
return subject, body
|
||||
}
|
||||
|
||||
func (h *AuthHandler) prepareKratosShortLogin(req *kratosCourierRequest, loginID string) (string, string, bool) {
|
||||
if req == nil || loginID == "" {
|
||||
return "", "", false
|
||||
}
|
||||
code := normalizeLoginCode(extractFirstString(req.TemplateData, "login_code"))
|
||||
if code == "" {
|
||||
return ""
|
||||
return "", "", false
|
||||
}
|
||||
shortCode := h.generateShortCode(code)
|
||||
if shortCode == "" {
|
||||
return ""
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
pendingRef, _ := h.RedisService.Get(prefixLoginCodePending + loginID)
|
||||
@@ -1629,7 +1664,7 @@ func (h *AuthHandler) buildKratosShortSmsBody(req *kratosCourierRequest, loginID
|
||||
}
|
||||
|
||||
link := fmt.Sprintf("%s/l/%s", baseURL, shortCode)
|
||||
return fmt.Sprintf("[Baron 통합로그인] %s", link)
|
||||
return shortCode, link, true
|
||||
}
|
||||
|
||||
func (h *AuthHandler) generateShortCode(code string) string {
|
||||
|
||||
Reference in New Issue
Block a user