VYPR
Low severity3.1NVD Advisory· Published May 18, 2026· Updated May 18, 2026

CVE-2026-6334

CVE-2026-6334

Description

Mattermost versions 11.5.x <= 11.5.1, 10.11.x <= 10.11.13 fail to enforce client identity binding during the OAuth authorization code redemption flow which allows an authenticated OAuth client to redeem authorization codes issued to a different client via a crafted token exchange request.. Mattermost Advisory ID: MMSA-2026-00570

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Mattermost fails to enforce client identity binding during OAuth authorization code redemption, allowing authenticated OAuth clients to redeem codes issued to other clients.

Vulnerability

Mattermost versions 11.5.x up to and including 11.5.1, and 10.11.x up to and including 10.11.13, do not enforce client identity binding during the OAuth authorization code redemption flow [1]. This means that when a legitimate OAuth client obtains an authorization code, an authenticated but different OAuth client can exchange that code for tokens by crafting a token exchange request without the correct client_id validation.

Exploitation

An attacker must be an authenticated OAuth client within the Mattermost instance (i.e., have valid client credentials). The attacker intercepts or obtains an authorization code that was issued to another client (e.g., via a phishing or misconfiguration) and then sends a crafted token exchange request to the Mattermost server, substituting their own client credentials but using the victim's authorization code. The server fails to verify that the client redeeming the code matches the client to which the code was originally issued.

Impact

An attacker can redeem authorization codes intended for other OAuth clients, potentially gaining access tokens that grant privileges or access scopes associated with the victim client. The impact is limited to authentication bypass in OAuth flows; the attacker does not gain direct system access beyond what the victim client is authorized for.

Mitigation

According to Mattermost Security Advisory MMSA-2026-00570 [1], no specific fixed version is provided in the public advisory. Users should monitor the Mattermost security updates page for the release of a patched version. As a workaround, restrict OAuth client registration and monitor token exchange requests.

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

1
aa53aca3487f

oauth check (#35553) (#35684)

https://github.com/mattermost/mattermostMattermost BuildMar 19, 2026Fixed in 11.5.2via llm-release-walk
3 files changed · +70 0
  • server/channels/app/oauth.go+8 0 modified
    @@ -341,6 +341,10 @@ func (a *App) handleAuthorizationCodeGrant(rctx request.CTX, oauthApp *model.OAu
     		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusForbidden)
     	}
     
    +	if authData.ClientId != clientId {
    +		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.client_id_mismatch.app_error", nil, "", http.StatusBadRequest)
    +	}
    +
     	if authData.RedirectUri != redirectURI {
     		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.redirect_uri.app_error", nil, "", http.StatusBadRequest)
     	}
    @@ -395,6 +399,10 @@ func (a *App) handleRefreshTokenGrant(rctx request.CTX, oauthApp *model.OAuthApp
     		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.refresh_token.app_error", nil, "", http.StatusNotFound).Wrap(nErr)
     	}
     
    +	if accessData.ClientId != oauthApp.Id {
    +		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.client_id_mismatch.app_error", nil, "", http.StatusBadRequest)
    +	}
    +
     	user, nErr := a.Srv().Store().User().Get(context.Background(), accessData.UserId)
     	if nErr != nil {
     		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_user.app_error", nil, "", http.StatusNotFound).Wrap(nErr)
    
  • server/channels/app/oauth_test.go+58 0 modified
    @@ -1400,6 +1400,64 @@ func TestGetOAuthAccessTokenForCodeFlow(t *testing.T) {
     			require.Contains(t, appErr.Id, "resource_mismatch")
     		})
     	})
    +
    +	t.Run("DifferentClient_CannotRedeemCode", func(t *testing.T) {
    +		appA := createConfidentialOAuthApp("TestClientA")
    +		appB := createConfidentialOAuthApp("TestClientB")
    +		code := getAuthorizationCode(appA, "")
    +
    +		_, appErr := th.App.GetOAuthAccessTokenForCodeFlow(
    +			th.Context,
    +			appB.Id,
    +			model.AccessTokenGrantType,
    +			appA.CallbackUrls[0],
    +			code,
    +			appB.ClientSecret,
    +			"",
    +			"",
    +			"",
    +		)
    +		require.NotNil(t, appErr)
    +		require.Contains(t, appErr.Id, "client_id_mismatch")
    +		require.Equal(t, http.StatusBadRequest, appErr.StatusCode)
    +	})
    +
    +	t.Run("DifferentClient_CannotUseRefreshToken", func(t *testing.T) {
    +		appA := createConfidentialOAuthApp("TestClientA")
    +		appB := createConfidentialOAuthApp("TestClientB")
    +		code := getAuthorizationCode(appA, "")
    +
    +		// Get a valid refresh token for appA
    +		tokenResp, appErr := th.App.GetOAuthAccessTokenForCodeFlow(
    +			th.Context,
    +			appA.Id,
    +			model.AccessTokenGrantType,
    +			appA.CallbackUrls[0],
    +			code,
    +			appA.ClientSecret,
    +			"",
    +			"",
    +			"",
    +		)
    +		require.Nil(t, appErr)
    +		require.NotEmpty(t, tokenResp.RefreshToken)
    +
    +		// Try to use appA's refresh token with appB's credentials
    +		_, appErr = th.App.GetOAuthAccessTokenForCodeFlow(
    +			th.Context,
    +			appB.Id,
    +			model.RefreshTokenGrantType,
    +			appB.CallbackUrls[0],
    +			"",
    +			appB.ClientSecret,
    +			tokenResp.RefreshToken,
    +			"",
    +			"",
    +		)
    +		require.NotNil(t, appErr)
    +		require.Contains(t, appErr.Id, "client_id_mismatch")
    +		require.Equal(t, http.StatusBadRequest, appErr.StatusCode)
    +	})
     }
     func TestParseOAuthStateTokenExtra(t *testing.T) {
     	t.Run("valid token with normal values", func(t *testing.T) {
    
  • server/i18n/en.json+4 0 modified
    @@ -2576,6 +2576,10 @@
         "id": "api.oauth.get_access_token.bad_request.app_error",
         "translation": "invalid_request: Bad request."
       },
    +  {
    +    "id": "api.oauth.get_access_token.client_id_mismatch.app_error",
    +    "translation": "invalid_grant: Token grant was not issued to this client."
    +  },
       {
         "id": "api.oauth.get_access_token.credentials.app_error",
         "translation": "invalid_client: Invalid client credentials."
    

Vulnerability mechanics

Root cause

"Missing client identity binding check during OAuth authorization code and refresh token redemption allows one authenticated client to redeem tokens issued to a different client."

Attack vector

An attacker who controls an authenticated OAuth client (client B) can craft a token exchange request using an authorization code or refresh token that was issued to a different client (client A). The request must include client B's own client ID and client secret, but supply the authorization code or refresh token that belongs to client A. Before the patch, the server did not verify that the client ID in the token request matched the client ID stored with the authorization code or refresh token [CWE-305]. This attack requires the attacker to first obtain a valid authorization code or refresh token intended for another client, and to have valid credentials for their own OAuth client.

Affected code

The vulnerability exists in `server/channels/app/oauth.go` in two functions: `handleAuthorizationCodeGrant` (around line 341) and `handleRefreshTokenGrant` (around line 395). Both functions lacked a check comparing the client ID associated with the stored authorization data against the client ID presented in the token exchange request.

What the fix does

The patch adds two identity-binding checks in `server/channels/app/oauth.go`. In `handleAuthorizationCodeGrant`, a new comparison verifies that `authData.ClientId` (the client ID stored with the authorization code) matches the `clientId` parameter from the incoming request [patch_id=918568]. Similarly, in `handleRefreshTokenGrant`, the patch checks that `accessData.ClientId` (the client ID stored with the refresh token) matches `oauthApp.Id`. If either check fails, the function returns a new `client_id_mismatch` error. These checks ensure that only the OAuth client to which a code or token was originally issued can redeem it, closing the authentication bypass.

Preconditions

  • authAttacker must have valid credentials (client ID and client secret) for an authenticated OAuth client (client B).
  • inputAttacker must obtain a valid authorization code or refresh token that was issued to a different OAuth client (client A).
  • networkThe attacker must be able to send crafted HTTP requests to the Mattermost OAuth token endpoint.

Generated on May 20, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

1

News mentions

0

No linked articles in our index yet.