forked from baron/baron-sso
SMS 발송 및 Redis 기반 인증 코드 검증, JWT 발급 기능 구현
This commit is contained in:
@@ -15,11 +15,13 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
|
||||
type AuthHandler struct {
|
||||
ProjectID string
|
||||
SmsService domain.SmsService
|
||||
ProjectID string
|
||||
SmsService domain.SmsService
|
||||
RedisService *service.RedisService
|
||||
}
|
||||
|
||||
func NewAuthHandler() *AuthHandler {
|
||||
@@ -28,9 +30,15 @@ func NewAuthHandler() *AuthHandler {
|
||||
// Fallback for dev if not set
|
||||
pid = "P37DsGepBT6uDWb5TYYpb5RxUPuq"
|
||||
}
|
||||
redisService, err := service.NewRedisService()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to Redis: %v", err)
|
||||
}
|
||||
|
||||
return &AuthHandler{
|
||||
ProjectID: pid,
|
||||
SmsService: service.NewSmsService(),
|
||||
ProjectID: pid,
|
||||
SmsService: service.NewSmsService(),
|
||||
RedisService: redisService,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,24 +49,94 @@ func (h *AuthHandler) SendSms(c *fiber.Ctx) error {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
|
||||
}
|
||||
|
||||
log.Printf("[SMS 발송 시작] 요청된 번호: %s", req.PhoneNumber)
|
||||
|
||||
// Sanitize phone number: remove dashes
|
||||
sanitizedPhone := strings.ReplaceAll(req.PhoneNumber, "-", "")
|
||||
log.Printf("[SMS 발송] 번호 정제 완료: %s", sanitizedPhone)
|
||||
|
||||
// Generate a 6-digit verification code
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
code := fmt.Sprintf("%06d", rand.Intn(1000000))
|
||||
content := fmt.Sprintf("[Baron SSO] Your verification code is %s", code)
|
||||
log.Printf("[SMS 발송] 인증 코드 생성 완료: %s", code)
|
||||
|
||||
// Store the code in Redis before sending
|
||||
if err := h.RedisService.StoreVerificationCode(sanitizedPhone, code); err != nil {
|
||||
log.Printf("[SMS 발송 실패] Redis에 코드 저장 실패: %v", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to process request"})
|
||||
}
|
||||
log.Printf("[SMS 발송] Redis에 인증 코드 저장 성공 (키: sms_verify:%s)", sanitizedPhone)
|
||||
|
||||
if err := h.SmsService.SendSms(sanitizedPhone, content); err != nil {
|
||||
log.Printf("Error sending SMS: %v", err)
|
||||
log.Printf("[SMS 발송 실패] SENS API 호출 실패: %v", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send SMS"})
|
||||
}
|
||||
|
||||
// TODO: Store the verification code for later verification
|
||||
log.Printf("[SMS 발송 성공] SENS API를 통해 SMS 발송 완료")
|
||||
|
||||
return c.JSON(fiber.Map{"message": "SMS sent successfully"})
|
||||
}
|
||||
|
||||
// VerifySms verifies the provided SMS code.
|
||||
func (h *AuthHandler) VerifySms(c *fiber.Ctx) error {
|
||||
var req domain.SmsVerifyRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
|
||||
}
|
||||
|
||||
log.Printf("[SMS 검증 시작] 요청된 번호: %s, 코드: %s", req.PhoneNumber, req.Code)
|
||||
|
||||
sanitizedPhone := strings.ReplaceAll(req.PhoneNumber, "-", "")
|
||||
log.Printf("[SMS 검증] 번호 정제 완료: %s", sanitizedPhone)
|
||||
|
||||
storedCode, err := h.RedisService.GetVerificationCode(sanitizedPhone)
|
||||
if err != nil {
|
||||
log.Printf("[SMS 검증 실패] Redis에서 코드 조회 실패: %v", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal server error"})
|
||||
}
|
||||
log.Printf("[SMS 검증] Redis에서 코드 조회 완료. 저장된 코드: '%s'", storedCode)
|
||||
|
||||
if storedCode == "" || storedCode != req.Code {
|
||||
log.Printf("[SMS 검증 실패] 코드가 일치하지 않거나 만료됨 (요청된 코드: %s, 저장된 코드: %s)", req.Code, storedCode)
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid or expired code"})
|
||||
}
|
||||
log.Printf("[SMS 검증] 코드 일치 확인")
|
||||
|
||||
// Code is correct, delete it to prevent reuse
|
||||
if err := h.RedisService.DeleteVerificationCode(sanitizedPhone); err != nil {
|
||||
// Log the error but don't fail the request as the code was already verified
|
||||
log.Printf("[SMS 검증] 경고: Redis에서 코드 삭제 실패 (하지만 검증은 성공으로 처리됨): %v", err)
|
||||
} else {
|
||||
log.Printf("[SMS 검증] Redis에서 사용된 코드 삭제 완료")
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
claims := jwt.MapClaims{
|
||||
"sub": sanitizedPhone, // Subject (user identifier)
|
||||
"exp": time.Now().Add(time.Hour * 24).Unix(), // Expiration time (24 hours)
|
||||
"iat": time.Now().Unix(), // Issued at
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
log.Printf("[SMS 검증] JWT 클레임 생성 완료")
|
||||
|
||||
// Sign the token with the secret key
|
||||
secretKey := os.Getenv("COOKIE_SECRET")
|
||||
if secretKey == "" {
|
||||
log.Println("Warning: COOKIE_SECRET is not set. Using a default, insecure key.")
|
||||
secretKey = "default-insecure-secret-key-for-dev"
|
||||
}
|
||||
|
||||
signedToken, err := token.SignedString([]byte(secretKey))
|
||||
if err != nil {
|
||||
log.Printf("[SMS 검증 실패] JWT 토큰 서명 실패: %v", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to generate token"})
|
||||
}
|
||||
|
||||
log.Printf("[SMS 검증 성공] JWT 토큰 발급 완료")
|
||||
return c.JSON(fiber.Map{"token": signedToken})
|
||||
}
|
||||
|
||||
// getBaseURL extracts the region code from Project ID if present (e.g., P37... -> api.37ds.descope.com)
|
||||
// Default is api.descope.com
|
||||
func (h *AuthHandler) getBaseURL() string {
|
||||
|
||||
Reference in New Issue
Block a user