forked from baron/baron-sso
namecard 연동
This commit is contained in:
220
backend/internal/handler/auth_handler.go
Normal file
220
backend/internal/handler/auth_handler.go
Normal file
@@ -0,0 +1,220 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"baron-sso-backend/internal/domain"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type AuthHandler struct {
|
||||
ProjectID string
|
||||
}
|
||||
|
||||
func NewAuthHandler() *AuthHandler {
|
||||
pid := os.Getenv("DESCOPE_PROJECT_ID")
|
||||
if pid == "" {
|
||||
// Fallback for dev if not set
|
||||
pid = "P37DsGepBT6uDWb5TYYpb5RxUPuq"
|
||||
}
|
||||
return &AuthHandler{ProjectID: pid}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if len(h.ProjectID) >= 32 {
|
||||
// Heuristic: Descope project IDs usually start with 'P'
|
||||
// If it's a region-specific project, the URL changes.
|
||||
// For P37DsGepBT6uDWb5TYYpb5RxUPuq, the region is likely '37ds'.
|
||||
// Actually, the safest bet is to use the standard API or check the logic.
|
||||
// The error log showed 'api.37ds.descope.com'.
|
||||
// Let's implement dynamic extraction or just use the standard one which redirects?
|
||||
// No, standard is safer if region is unsure, but let's try to match the error URL.
|
||||
// Region code is usually the first 4 chars after P? No.
|
||||
// Let's rely on standard logic: https://api.descope.com usually works and routes.
|
||||
// BUT the user specifically saw api.37ds.descope.com.
|
||||
// Let's try the generic endpoint first.
|
||||
return "https://api.descope.com"
|
||||
}
|
||||
return "https://api.descope.com"
|
||||
}
|
||||
|
||||
// InitEnchantedLink proxies the sign-up/in request
|
||||
func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
|
||||
var req domain.EnchantedLinkInitRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
fmt.Printf("[DEBUG] BodyParser failed: %v\n", err)
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
|
||||
}
|
||||
|
||||
fmt.Printf("[DEBUG] InitEnchantedLink - Received LoginID: '%s', URI: '%s'\n", req.LoginID, req.URI)
|
||||
|
||||
// Prepare Descope Request
|
||||
// Note: We are using the public API endpoint which expects Bearer <ProjectID>
|
||||
|
||||
// Determine endpoint type (email vs sms)
|
||||
// Default to Enchanted Link Email
|
||||
apiPath := "enchantedlink/signup-in/email"
|
||||
|
||||
if req.Method == "sms" {
|
||||
apiPath = "magiclink/signup-in/sms"
|
||||
} else if len(req.LoginID) > 0 && req.LoginID[0] == '+' {
|
||||
// Auto-detect if starts with +
|
||||
apiPath = "magiclink/signup-in/sms"
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/v1/auth/%s", h.getBaseURL(), apiPath)
|
||||
|
||||
payload := map[string]string{
|
||||
"loginId": req.LoginID,
|
||||
// "redirectUrl": req.URI, // Let Descope use default from console configuration
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
|
||||
r, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
r.Header.Set("Authorization", "Bearer "+h.ProjectID)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(r)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadGateway).SendString(err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return c.Status(resp.StatusCode).Send(respBody)
|
||||
}
|
||||
|
||||
return c.Send(respBody)
|
||||
}
|
||||
|
||||
// PollEnchantedLink proxies the polling request
|
||||
func (h *AuthHandler) PollEnchantedLink(c *fiber.Ctx) error {
|
||||
var req domain.EnchantedLinkPollRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/v1/auth/enchantedlink/pending-session", h.getBaseURL())
|
||||
|
||||
payload := map[string]string{
|
||||
"pendingRef": req.PendingRef,
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
|
||||
r, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
r.Header.Set("Authorization", "Bearer "+h.ProjectID)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(r)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadGateway).SendString(err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return c.Status(resp.StatusCode).Send(respBody)
|
||||
}
|
||||
|
||||
return c.Send(respBody)
|
||||
}
|
||||
|
||||
// VerifyMagicLink verifies the token (t) from the email link
|
||||
|
||||
func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
|
||||
|
||||
var req domain.MagicLinkVerifyRequest
|
||||
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Use Magic Link Verify API
|
||||
|
||||
url := fmt.Sprintf("%s/v1/auth/magiclink/verify", h.getBaseURL())
|
||||
|
||||
|
||||
|
||||
payload := map[string]string{
|
||||
|
||||
"token": req.Token,
|
||||
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(payload)
|
||||
|
||||
|
||||
|
||||
r, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
|
||||
|
||||
if err != nil {
|
||||
|
||||
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
|
||||
r.Header.Set("Authorization", "Bearer "+h.ProjectID)
|
||||
|
||||
|
||||
|
||||
client := &http.Client{}
|
||||
|
||||
resp, err := client.Do(r)
|
||||
|
||||
if err != nil {
|
||||
|
||||
return c.Status(fiber.StatusBadGateway).SendString(err.Error())
|
||||
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
||||
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
|
||||
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
|
||||
return c.Status(resp.StatusCode).Send(respBody)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
return c.Send(respBody)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user