* MM 65084 server-side (#33861) (#34006)

Automatic Merge

* Add ConsumeOnce method to store layers

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
JG Heithcock
2025-10-06 15:20:08 -07:00
коммит произвёл GitHub
родитель 4b56488fcb
Коммит 375ce229f4
11 изменённых файлов: 309 добавлений и 5 удалений

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

@@ -37,6 +37,10 @@ func loginWithSaml(c *Context, w http.ResponseWriter, r *http.Request) {
action := r.URL.Query().Get("action")
isMobile := action == model.OAuthActionMobile
redirectURL := html.EscapeString(r.URL.Query().Get("redirect_to"))
// Optional SAML challenge parameters for mobile code-exchange
state := r.URL.Query().Get("state")
codeChallenge := r.URL.Query().Get("code_challenge")
codeChallengeMethod := r.URL.Query().Get("code_challenge_method")
relayProps := map[string]string{}
relayState := ""
@@ -61,6 +65,19 @@ func loginWithSaml(c *Context, w http.ResponseWriter, r *http.Request) {
relayProps["redirect_to"] = redirectURL
}
// Forward SAML challenge values via RelayState so the complete step can prefer code-exchange
if isMobile {
if state != "" {
relayProps["state"] = state
}
if codeChallenge != "" {
relayProps["code_challenge"] = codeChallenge
}
if codeChallengeMethod != "" {
relayProps["code_challenge_method"] = codeChallengeMethod
}
}
desktopToken := r.URL.Query().Get("desktop_token")
if desktopToken != "" {
relayProps["desktop_token"] = desktopToken
@@ -220,7 +237,33 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
// If it's not a desktop login we create a session for this SAML User that will be used in their browser or mobile app
// Decide between legacy token-in-URL vs SAML code-exchange for mobile
samlState := relayProps["state"]
samlChallenge := relayProps["code_challenge"]
samlMethod := relayProps["code_challenge_method"]
if isMobile && hasRedirectURL && samlChallenge != "" && c.App.Config().FeatureFlags.MobileSSOCodeExchange {
// Issue one-time login_code bound to user and SAML challenge values; do not create a session here
extra := model.MapToJSON(map[string]string{
"user_id": user.Id,
"state": samlState,
"code_challenge": samlChallenge,
"code_challenge_method": samlMethod,
})
code := model.NewToken(model.TokenTypeSaml, extra)
if err := c.App.Srv().Store().Token().Save(code); err != nil {
handleError(model.NewAppError("completeSaml", "app.recover.save.app_error", nil, "", http.StatusInternalServerError).Wrap(err))
return
}
redirectURL = utils.AppendQueryParamsToURL(redirectURL, map[string]string{
"login_code": code.Token,
})
utils.RenderMobileAuthComplete(w, redirectURL)
return
}
// Legacy: create a session and attach tokens (web/mobile without SAML code exchange)
session, err := c.App.DoLogin(c.AppContext, w, r, user, "", isMobile, false, true)
if err != nil {
handleError(err)
@@ -235,10 +278,13 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
if hasRedirectURL {
if isMobile {
// Mobile clients with redirect url support
redirectURL = utils.AppendQueryParamsToURL(redirectURL, map[string]string{
model.SessionCookieToken: c.AppContext.Session().Token,
model.SessionCookieCsrf: c.AppContext.Session().GetCSRF(),
})
// 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(),
})
}
utils.RenderMobileAuthComplete(w, redirectURL)
} else {
http.Redirect(w, r, redirectURL, http.StatusFound)