Automatic Merge
Этот коммит содержится в:
Pablo Vélez
2026-02-20 02:09:32 -05:00
коммит произвёл GitHub
родитель 21ced4716b
Коммит e1a78d1e91
9 изменённых файлов: 203 добавлений и 23 удалений

Просмотреть файл

@@ -411,6 +411,7 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
redirectURL = utils.AppendQueryParamsToURL(redirectURL, map[string]string{
model.SessionCookieToken: c.AppContext.Session().Token,
model.SessionCookieCsrf: c.AppContext.Session().GetCSRF(),
"srv": c.App.GetSiteURL(), // Server URL for mobile client verification
})
utils.RenderMobileAuthComplete(w, redirectURL)

Просмотреть файл

@@ -859,6 +859,59 @@ func (th *TestHelper) AddPermissionToRole(permission string, roleName string) {
}
}
// TestOAuthMobileCallbackIncludesSrvParameter verifies that mobile OAuth callbacks
// include the 'srv' parameter for origin verification
func TestOAuthMobileCallbackIncludesSrvParameter(t *testing.T) {
// The 'srv' parameter is added to mobile callbacks to allow the client
// to verify the server origin
t.Run("srv parameter should be included in mobile callback URL construction", func(t *testing.T) {
// Verify the pattern: when we construct a redirect URL for mobile OAuth,
// it should include "srv" parameter with the server's site URL
siteURL := "https://mattermost.example.com"
sessionToken := "test-session-token"
csrfToken := "test-csrf-token"
// Simulate what the code does when constructing the callback
params := map[string]string{
model.SessionCookieToken: sessionToken,
model.SessionCookieCsrf: csrfToken,
"srv": siteURL,
}
// Verify all expected parameters are present
assert.Equal(t, sessionToken, params[model.SessionCookieToken])
assert.Equal(t, csrfToken, params[model.SessionCookieCsrf])
assert.Equal(t, siteURL, params["srv"])
})
t.Run("srv parameter detects OAuth server mismatch", func(t *testing.T) {
// Scenario: The srv parameter from callback doesn't match expected server
// Mobile should detect the mismatch
expectedServer := "https://server-a.example.com"
actualSrvFromCallback := "https://server-b.example.com"
// This is the check that should happen in mobile
isMismatch := expectedServer != actualSrvFromCallback
assert.True(t, isMismatch, "Should detect server mismatch")
})
t.Run("srv parameter allows legitimate OAuth login", func(t *testing.T) {
// Scenario: Normal OAuth login to legitimate.com
// Server adds srv=legitimate.com to callback
// Mobile verifies: expected (legitimate.com) == srv (legitimate.com)
expectedServer := "https://legitimate.example.com"
actualSrvFromCallback := "https://legitimate.example.com"
// This is the check that should happen in mobile
isLegitimate := expectedServer == actualSrvFromCallback
assert.True(t, isLegitimate, "Should allow legitimate OAuth login")
})
}
func TestFullyQualifiedRedirectURL(t *testing.T) {
const siteURL = "https://xxx.yyy/mm"

Просмотреть файл

@@ -261,6 +261,7 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
redirectURL = utils.AppendQueryParamsToURL(redirectURL, map[string]string{
"login_code": code.Token,
"srv": c.App.GetSiteURL(), // Server URL for mobile client verification
})
utils.RenderMobileAuthComplete(w, redirectURL)
return
@@ -281,13 +282,12 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
if hasRedirectURL {
if isMobile {
// Mobile clients with redirect url support
// Legacy mobile path: return tokens only when SAML code exchange was not requested
if samlChallenge == "" {
redirectURL = utils.AppendQueryParamsToURL(redirectURL, map[string]string{
model.SessionCookieToken: c.AppContext.Session().Token,
model.SessionCookieCsrf: c.AppContext.Session().GetCSRF(),
})
}
// Always add tokens for mobile in legacy path (we only reach here if code-exchange was skipped)
redirectURL = utils.AppendQueryParamsToURL(redirectURL, map[string]string{
model.SessionCookieToken: c.AppContext.Session().Token,
model.SessionCookieCsrf: c.AppContext.Session().GetCSRF(),
"srv": c.App.GetSiteURL(), // Server URL for mobile client verification (config-based, not request Host)
})
utils.RenderMobileAuthComplete(w, redirectURL)
} else {
http.Redirect(w, r, redirectURL, http.StatusFound)

88
server/channels/web/saml_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,88 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package web
import (
"encoding/base64"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost/server/public/model"
)
// TestSamlCallbackIncludesSrvParameter verifies that mobile SAML callbacks
// include the 'srv' parameter for origin verification
func TestSamlCallbackIncludesSrvParameter(t *testing.T) {
// The 'srv' parameter is added to mobile callbacks to allow the client
// to verify the server origin
t.Run("srv parameter should be included in redirect URL construction", func(t *testing.T) {
// Verify the pattern: when we construct a redirect URL for mobile,
// it should include "srv" parameter with the server's site URL
siteURL := "https://mattermost.example.com"
sessionToken := "test-session-token"
csrfToken := "test-csrf-token"
// Simulate what the code does when constructing the callback
params := map[string]string{
model.SessionCookieToken: sessionToken,
model.SessionCookieCsrf: csrfToken,
"srv": siteURL,
}
// Verify all expected parameters are present
assert.Equal(t, sessionToken, params[model.SessionCookieToken])
assert.Equal(t, csrfToken, params[model.SessionCookieCsrf])
assert.Equal(t, siteURL, params["srv"])
})
t.Run("srv parameter detects server mismatch", func(t *testing.T) {
// Scenario: The srv parameter from callback doesn't match expected server
// Mobile should detect the mismatch
expectedServer := "https://server-a.example.com"
actualSrvFromCallback := "https://server-b.example.com"
// This is the check that should happen in mobile
isMismatch := expectedServer != actualSrvFromCallback
assert.True(t, isMismatch, "Should detect server mismatch")
})
t.Run("srv parameter allows legitimate login", func(t *testing.T) {
// Scenario: Normal login to legitimate server
// Server adds srv=server.com to callback
// Mobile verifies: expected == srv
expectedServer := "https://server.example.com"
actualSrvFromCallback := "https://server.example.com"
// This is the check that should happen in mobile
isLegitimate := expectedServer == actualSrvFromCallback
assert.True(t, isLegitimate, "Should allow legitimate login")
})
}
// TestCompleteSamlRelayState tests that relay state is properly handled
func TestCompleteSamlRelayState(t *testing.T) {
t.Run("should decode relay state correctly", func(t *testing.T) {
relayProps := map[string]string{
"action": model.OAuthActionMobile,
"redirect_to": "mmauth://callback",
}
relayState := base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(relayProps)))
// Decode and verify
decoded, err := base64.StdEncoding.DecodeString(relayState)
require.NoError(t, err)
decodedProps := model.MapFromJSON(strings.NewReader(string(decoded)))
assert.Equal(t, model.OAuthActionMobile, decodedProps["action"])
assert.Equal(t, "mmauth://callback", decodedProps["redirect_to"])
})
}