1
0
forked from baron/baron-sso

Fix SMS login code flow for phone relay

This commit is contained in:
2026-05-07 13:53:23 +09:00
parent 404e5179e8
commit 57a00c0236
4 changed files with 79 additions and 7 deletions

View File

@@ -1028,6 +1028,13 @@ func (h *AuthHandler) resolveUserfrontURL(c *fiber.Ctx) string {
envParsed.Scheme == "https" && baseParsed.Scheme == "http" {
return strings.TrimRight(envURL, "/")
}
if os.Getenv("APP_ENV") == "dev" &&
envErr == nil && baseErr == nil &&
strings.EqualFold(envParsed.Hostname(), baseParsed.Hostname()) &&
(envParsed.Hostname() == "localhost" || envParsed.Hostname() == "127.0.0.1") &&
envParsed.Port() != "" && baseParsed.Port() == "" {
return strings.TrimRight(envURL, "/")
}
return baseURL
}
@@ -2003,6 +2010,13 @@ func (h *AuthHandler) VerifyLoginCode(c *fiber.Ctx) error {
if !strings.Contains(loginID, "@") {
lookupLoginID = normalizePhoneForLoginID(loginID)
}
smsLookupLoginID := ""
if !strings.Contains(loginID, "@") {
smsLookupLoginID = lookupLoginID
if mapped, _ := h.RedisService.Get(prefixLoginCodeSmsLookup + smsLookupLoginID); mapped != "" {
lookupLoginID = mapped
}
}
if h.IdpProvider == nil {
return errorJSONCode(c, fiber.StatusServiceUnavailable, "service_unavailable", "Identity provider unavailable")
@@ -2016,11 +2030,6 @@ func (h *AuthHandler) VerifyLoginCode(c *fiber.Ctx) error {
if req.VerifyOnly {
c.Locals("auth_timeline_skip", true)
effectiveLoginID := lookupLoginID
if !strings.Contains(loginID, "@") {
if mapped, _ := h.RedisService.Get(prefixLoginCodeSmsLookup + lookupLoginID); mapped != "" {
effectiveLoginID = mapped
}
}
pendingRef := strings.TrimSpace(req.PendingRef)
storedRef, _ := h.RedisService.Get(prefixLoginCodePending + lookupLoginID)
if pendingRef == "" {
@@ -2075,6 +2084,9 @@ func (h *AuthHandler) VerifyLoginCode(c *fiber.Ctx) error {
h.RedisService.Delete(prefixLoginCode + lookupLoginID)
h.RedisService.Delete(prefixLoginCodeSmsTarget + lookupLoginID)
if smsLookupLoginID != "" {
h.RedisService.Delete(prefixLoginCodeSmsLookup + smsLookupLoginID)
}
pendingRef := strings.TrimSpace(req.PendingRef)
if pendingRef == "" {
@@ -2089,6 +2101,9 @@ func (h *AuthHandler) VerifyLoginCode(c *fiber.Ctx) error {
h.RedisService.Set(prefixSession+pendingRef, string(sessionData), loginCodeExpiration)
h.RedisService.Delete(prefixLoginCodePending + lookupLoginID)
h.RedisService.Delete(prefixLoginCodeSmsTarget + lookupLoginID)
if smsLookupLoginID != "" {
h.RedisService.Delete(prefixLoginCodeSmsLookup + smsLookupLoginID)
}
return c.JSON(fiber.Map{
"status": "approved",
"pendingRef": pendingRef,

View File

@@ -6,6 +6,7 @@ import (
"baron-sso-backend/internal/testsupport"
"bytes"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"strings"
@@ -150,6 +151,56 @@ func TestEnchantedLinkFlow_Sms_Success(t *testing.T) {
assert.NotEmpty(t, initResp["userCode"])
}
func TestResolveUserfrontURL_DevLocalhostUsesConfiguredPort(t *testing.T) {
t.Setenv("APP_ENV", "dev")
t.Setenv("USERFRONT_URL", "http://localhost:5000")
h := &AuthHandler{}
app := fiber.New()
app.Get("/probe", func(c *fiber.Ctx) error {
return c.SendString(h.resolveUserfrontURL(c))
})
req := httptest.NewRequest(http.MethodGet, "http://localhost/probe", nil)
resp, _ := app.Test(req, -1)
assert.Equal(t, http.StatusOK, resp.StatusCode)
body, _ := io.ReadAll(resp.Body)
assert.Equal(t, "http://localhost:5000", string(body))
}
func TestVerifyLoginCode_MapsSmsPhoneBeforeFlowLookup(t *testing.T) {
redis := &mockRedisRepo{data: map[string]string{
prefixLoginCode + "su-@samaneng.com": "flow-123",
prefixLoginCodePending + "su-@samaneng.com": "pending-123",
prefixLoginCodeSmsLookup + "+821041585840": "su-@samaneng.com",
prefixLoginCodeSmsTarget + "su-@samaneng.com": "+821041585840",
prefixLoginCodeValue + "pending-123": "569765",
}}
h := &AuthHandler{
RedisService: redis,
IdpProvider: &mockIdpProvider{},
}
app := fiber.New()
app.Post("/api/v1/auth/login/code/verify", h.VerifyLoginCode)
body, _ := json.Marshal(map[string]interface{}{
"loginId": "01041585840",
"code": "569765",
"pendingRef": "pending-123",
"verifyOnly": true,
})
req := httptest.NewRequest(http.MethodPost, "/api/v1/auth/login/code/verify", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
resp, _ := app.Test(req, -1)
assert.Equal(t, http.StatusOK, resp.StatusCode)
var got map[string]interface{}
_ = json.NewDecoder(resp.Body).Decode(&got)
assert.Equal(t, "approved", got["status"])
assert.Equal(t, "pending-123", got["pendingRef"])
}
func TestPollEnchantedLink_ExpiredToken_ReturnsCode(t *testing.T) {
redis := &mockRedisRepo{data: make(map[string]string)}
h := &AuthHandler{