forked from baron/baron-sso
Fix SMS login code flow for phone relay
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -65,14 +65,14 @@ OATHKEEPER_INTROSPECT_CLIENT_SECRET="${OATHKEEPER_INTROSPECT_CLIENT_SECRET:-oath
|
||||
export KRATOS_DSN HYDRA_DSN KETO_DSN HYDRA_SYSTEM_SECRET
|
||||
export OATHKEEPER_INTROSPECT_CLIENT_ID OATHKEEPER_INTROSPECT_CLIENT_SECRET
|
||||
|
||||
rm -rf "$OUTPUT_DIR"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
mkdir -p "$OUTPUT_DIR/kratos" "$OUTPUT_DIR/hydra" "$OUTPUT_DIR/keto" "$OUTPUT_DIR/oathkeeper"
|
||||
|
||||
render_template "$TEMPLATE_ROOT/kratos/kratos.yml.template" "$OUTPUT_DIR/kratos/kratos.yml"
|
||||
copy_if_exists "$TEMPLATE_ROOT/kratos/identity.schema.json" "$OUTPUT_DIR/kratos/identity.schema.json"
|
||||
copy_if_exists "$TEMPLATE_ROOT/kratos/courier-http.jsonnet" "$OUTPUT_DIR/kratos/courier-http.jsonnet"
|
||||
if [[ -d "$TEMPLATE_ROOT/kratos/courier-templates" ]]; then
|
||||
mkdir -p "$OUTPUT_DIR/kratos"
|
||||
rm -rf "$OUTPUT_DIR/kratos/courier-templates"
|
||||
cp -a "$TEMPLATE_ROOT/kratos/courier-templates" "$OUTPUT_DIR/kratos/courier-templates"
|
||||
fi
|
||||
|
||||
@@ -85,6 +85,7 @@ copy_if_exists "$TEMPLATE_ROOT/keto/namespaces.yml" "$OUTPUT_DIR/keto/namespaces
|
||||
render_template "$TEMPLATE_ROOT/oathkeeper/oathkeeper.yml.template" "$OUTPUT_DIR/oathkeeper/oathkeeper.yml"
|
||||
copy_if_exists "$TEMPLATE_ROOT/oathkeeper/entrypoint.sh" "$OUTPUT_DIR/oathkeeper/entrypoint.sh"
|
||||
chmod +x "$OUTPUT_DIR/oathkeeper/entrypoint.sh"
|
||||
find "$OUTPUT_DIR/oathkeeper" -maxdepth 1 -type f -name 'rules*.json' -delete
|
||||
for rules_file in "$TEMPLATE_ROOT"/oathkeeper/rules*.json; do
|
||||
[[ -e "$rules_file" ]] || continue
|
||||
copy_if_exists "$rules_file" "$OUTPUT_DIR/oathkeeper/$(basename "$rules_file")"
|
||||
|
||||
@@ -274,6 +274,11 @@ if ! grep -q 'scripts/render_ory_config.sh' "$repo_root/.gitea/workflows/staging
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep -Eq '^[[:space:]]*rm -rf "?\$OUTPUT_DIR"?[[:space:]]*$' "$repo_root/scripts/render_ory_config.sh"; then
|
||||
echo "ERROR: Ory renderer must preserve config/.generated/ory service directories so live bind mounts stay valid." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"$repo_root/scripts/render_ory_config.sh" >/dev/null
|
||||
|
||||
for generated_config in \
|
||||
|
||||
Reference in New Issue
Block a user