forked from baron/baron-sso
qr 로그인
This commit is contained in:
@@ -132,7 +132,7 @@ func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
|
||||
|
||||
loginID := strings.ReplaceAll(req.LoginID, "-", "")
|
||||
loginID = strings.ReplaceAll(loginID, " ", "")
|
||||
|
||||
|
||||
// Generate secure tokens
|
||||
token := GenerateSecureToken(3)
|
||||
pendingRef := GenerateSecureToken(3)
|
||||
@@ -150,7 +150,7 @@ func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
|
||||
}
|
||||
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)
|
||||
|
||||
if err := h.SmsService.SendSms(loginID, content); err != nil {
|
||||
@@ -230,8 +230,8 @@ func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
|
||||
if strings.HasPrefix(searchPhone, "010") {
|
||||
searchPhone = "+82" + searchPhone[1:]
|
||||
} else if strings.HasPrefix(searchPhone, "82") {
|
||||
searchPhone = "+" + searchPhone
|
||||
}
|
||||
searchPhone = "+" + searchPhone
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[Verify] Searching for user with phone: %s", searchPhone)
|
||||
@@ -239,10 +239,10 @@ func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
|
||||
Phones: []string{searchPhone},
|
||||
Limit: 1,
|
||||
}
|
||||
|
||||
|
||||
var targetLoginID string
|
||||
users, _, errSearch := h.DescopeClient.Management.User().SearchAll(context.Background(), searchOptions)
|
||||
|
||||
|
||||
if errSearch == nil && len(users) > 0 {
|
||||
if len(users[0].LoginIDs) > 0 {
|
||||
targetLoginID = users[0].LoginIDs[0]
|
||||
@@ -264,7 +264,7 @@ func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
|
||||
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)
|
||||
|
||||
|
||||
// Create User with Explicit Phone Attribute
|
||||
userObj := &descope.UserRequest{}
|
||||
if strings.Contains(targetLoginID, "@") {
|
||||
@@ -278,7 +278,7 @@ func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
|
||||
log.Printf("[Verify] Failed to create user: %v", 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)
|
||||
@@ -304,12 +304,93 @@ func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
|
||||
"jwt": sessionToken,
|
||||
})
|
||||
h.RedisService.Set(prefixSession+pendingRef, string(sessionData), defaultExpiration)
|
||||
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"token": sessionToken,
|
||||
"message": "Login successful",
|
||||
})
|
||||
}
|
||||
|
||||
// InitQRLogin - Step 1: Web 패널에서 QR 로그인 세션을 생성합니다.
|
||||
func (h *AuthHandler) InitQRLogin(c *fiber.Ctx) error {
|
||||
pendingRef := GenerateSecureToken(16)
|
||||
|
||||
// QR 코드 페이로드를 실제 접속 가능한 URL로 변경합니다.
|
||||
frontendURL := os.Getenv("FRONTEND_URL")
|
||||
if frontendURL == "" {
|
||||
frontendURL = "https://ssologin.hmac.kr"
|
||||
}
|
||||
qrPayload := fmt.Sprintf("%s/approve?ref=%s", frontendURL, pendingRef)
|
||||
|
||||
log.Printf("[QR] Init: PendingRef=%s, URL=%s", pendingRef, qrPayload)
|
||||
|
||||
// Redis에 초기 상태 저장 (5분 만료)
|
||||
h.RedisService.Set(prefixSession+pendingRef, fmt.Sprintf(`{"status":"%s"}`, statusPending), 5*time.Minute)
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"qrCode": qrPayload, // 프론트엔드에서 이 텍스트로 QR을 생성하거나, 이미지를 반환
|
||||
"pendingRef": pendingRef,
|
||||
"expiresIn": 300,
|
||||
})
|
||||
}
|
||||
|
||||
// PollQRLogin - Step 2: 웹에서 승인 여부를 폴링합니다.
|
||||
func (h *AuthHandler) PollQRLogin(c *fiber.Ctx) error {
|
||||
var req struct {
|
||||
PendingRef string `json:"pendingRef"`
|
||||
}
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid body"})
|
||||
}
|
||||
|
||||
val, err := h.RedisService.Get(prefixSession + req.PendingRef)
|
||||
if err != nil || val == "" {
|
||||
return c.JSON(fiber.Map{"status": "expired"})
|
||||
}
|
||||
|
||||
var data map[string]string
|
||||
json.Unmarshal([]byte(val), &data)
|
||||
|
||||
if data["status"] == statusSuccess {
|
||||
return c.JSON(fiber.Map{
|
||||
"status": "ok",
|
||||
"sessionJwt": data["jwt"],
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{"status": statusPending})
|
||||
}
|
||||
|
||||
// ScanQRLogin - Step 3: 모바일 앱에서 QR 스캔 후 승인할 때 호출합니다.
|
||||
// (이미 로그인된 세션이 필요함)
|
||||
func (h *AuthHandler) ScanQRLogin(c *fiber.Ctx) error {
|
||||
var req struct {
|
||||
PendingRef string `json:"pendingRef"`
|
||||
Token string `json:"token"` // 모바일 사용자의 세션 토큰 (검증용)
|
||||
}
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid body"})
|
||||
}
|
||||
|
||||
log.Printf("[QR] Scan & Approve: PendingRef=%s", req.PendingRef)
|
||||
|
||||
// 1. Redis에서 세션 확인
|
||||
val, err := h.RedisService.Get(prefixSession + req.PendingRef)
|
||||
if err != nil || val == "" {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Session expired or not found"})
|
||||
}
|
||||
|
||||
// 2. 모바일 유저의 토큰으로 새 세션 토큰(웹용)을 발행하거나 그대로 전달
|
||||
|
||||
sessionData, _ := json.Marshal(map[string]string{
|
||||
"status": statusSuccess,
|
||||
"jwt": req.Token,
|
||||
})
|
||||
h.RedisService.Set(prefixSession+req.PendingRef, string(sessionData), 5*time.Minute)
|
||||
|
||||
return c.JSON(fiber.Map{"message": "QR Login Approved"})
|
||||
}
|
||||
|
||||
// ProxyToDescope (Placeholder)
|
||||
func (h *AuthHandler) ProxyToDescope(c *fiber.Ctx, path string, payload interface{}) error {
|
||||
return c.Status(501).SendString("Descope Proxy Disabled")
|
||||
|
||||
Reference in New Issue
Block a user