hunk.code_window: 	ShippingAddress *Address `json:"shipping_address"`
   	Organization    string   `json:"organization"`
   }
   type SelfHostedConfirmPaymentMethodRequest struct {
   	StripeSetupIntentID string                    `json:"stripe_setup_intent_id"`
   	Subscription        CreateSubscriptionRequest `json:"subscription"`
   }
   type SelfHostedExpansionConfirmPaymentMethodRequest struct {
   	StripeSetupIntentID string                     `json:"stripe_setup_intent_id"`
   	ExpandRequest       SelfHostedExpansionRequest `json:"expand_request"`
   }
   // SelfHostedSignupPaymentResponse contains feels needed for self hosted signup to confirm payment and receive license.
   type SelfHostedSignupCustomerResponse struct {
  
hunk.labels: keep keep keep keep keep replace replace replace replace replace replace replace keep keep keep keep
hunk.after_edit: 	StripeSetupIntentID string                      `json:"stripe_setup_intent_id"`
   	Subscription        *CreateSubscriptionRequest  `json:"subscription"`
   	ExpandRequest       *SelfHostedExpansionRequest `json:"expand_request"`
  
hunk.file_path: model/hosted_customer.go
hunk.type: replace
file: // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
  // See LICENSE.txt for license information.
  
  package web
  
  import (
  	"context"
  	"encoding/base64"
  	"encoding/json"
  	"io"
  	"net/http"
  	"net/http/httptest"
  	"net/url"
  	"strings"
  	"testing"
  
  	"github.com/stretchr/testify/assert"
  	"github.com/stretchr/testify/require"
  
  	"github.com/mattermost/mattermost-server/v6/model"
  	"github.com/mattermost/mattermost-server/v6/server/channels/einterfaces"
  	"github.com/mattermost/mattermost-server/v6/server/channels/utils"
  	"github.com/mattermost/mattermost-server/v6/server/platform/shared/i18n"
  	"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
  )
  
  func TestOAuthComplete_AccessDenied(t *testing.T) {
  	th := Setup(t).InitBasic()
  	defer th.TearDown()
  
  	c := &Context{
  		App: th.App,
  		Params: &Params{
  			Service: "TestService",
  		},
  	}
  	responseWriter := httptest.NewRecorder()
  	request, _ := http.NewRequest(http.MethodGet, th.App.GetSiteURL()+"/signup/TestService/complete?error=access_denied", nil)
  
  	completeOAuth(c, responseWriter, request)
  
  	response := responseWriter.Result()
  
  	assert.Equal(t, http.StatusTemporaryRedirect, response.StatusCode)
  
  	location, _ := url.Parse(response.Header.Get("Location"))
  	assert.Equal(t, "oauth_access_denied", location.Query().Get("type"))
  	assert.Equal(t, "TestService", location.Query().Get("service"))
  }
  
  func TestAuthorizeOAuthApp(t *testing.T) {
  	th := Setup(t).InitBasic()
  	th.Login(apiClient, th.SystemAdminUser)
  	defer th.TearDown()
  
  	enableOAuth := *th.App.Config().ServiceSettings.EnableOAuthServiceProvider
  	defer func() {
  		th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth })
  	}()
  
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true })
  
  	oapp := &model.OAuthApp{
  		Name:         GenerateTestAppName(),
  		Homepage:     "https://nowhere.com",
  		Description:  "test",
  		CallbackUrls: []string{"https://nowhere.com"},
  		CreatorId:    th.SystemAdminUser.Id,
  	}
  
  	rapp, appErr := th.App.CreateOAuthApp(oapp)
  	require.Nil(t, appErr)
  
  	authRequest := &model.AuthorizeRequest{
  		ResponseType: model.AuthCodeResponseType,
  		ClientId:     rapp.Id,
  		RedirectURI:  rapp.CallbackUrls[0],
  		Scope:        "",
  		State:        "123",
  	}
  
  	// Test auth code flow
  	ruri, _, err := apiClient.AuthorizeOAuthApp(authRequest)
  	require.NoError(t, err)
  
  	require.NotEmpty(t, ruri, "redirect url should be set")
  
  	ru, _ := url.Parse(ruri)
  	require.NotNil(t, ru, "redirect url unparseable")
  	require.NotEmpty(t, ru.Query().Get("code"), "authorization code not returned")
  	require.Equal(t, ru.Query().Get("state"), authRequest.State, "returned state doesn't match")
  
  	// Test implicit flow
  	authRequest.ResponseType = model.ImplicitResponseType
  	ruri, _, err = apiClient.AuthorizeOAuthApp(authRequest)
  	require.NoError(t, err)
  	require.False(t, ruri == "", "redirect url should be set")
  
  	ru, _ = url.Parse(ruri)
  	require.NotNil(t, ru, "redirect url unparseable")
  	values, err := url.ParseQuery(ru.Fragment)
  	require.NoError(t, err)
  	assert.False(t, values.Get("access_token") == "", "access_token not returned")
  	assert.Equal(t, authRequest.State, values.Get("state"), "returned state doesn't match")
  
  	oldToken := apiClient.AuthToken
  	apiClient.AuthToken = values.Get("access_token")
  	_, resp, err := apiClient.AuthorizeOAuthApp(authRequest)
  	require.Error(t, err)
  	CheckForbiddenStatus(t, resp)
  
  	apiClient.AuthToken = oldToken
  
  	authRequest.RedirectURI = ""
  	_, resp, err = apiClient.AuthorizeOAuthApp(authRequest)
  	require.Error(t, err)
  	CheckBadRequestStatus(t, resp)
  
  	authRequest.RedirectURI = "http://somewhereelse.com"
  	_, resp, err = apiClient.AuthorizeOAuthApp(authRequest)
  	require.Error(t, err)
  	CheckBadRequestStatus(t, resp)
  
  	authRequest.RedirectURI = rapp.CallbackUrls[0]
  	authRequest.ResponseType = ""
  	_, resp, err = apiClient.AuthorizeOAuthApp(authRequest)
  	require.Error(t, err)
  	CheckBadRequestStatus(t, resp)
  
  	authRequest.ResponseType = model.AuthCodeResponseType
  	authRequest.ClientId = ""
  	_, resp, err = apiClient.AuthorizeOAuthApp(authRequest)
  	require.Error(t, err)
  	CheckBadRequestStatus(t, resp)
  
  	authRequest.ClientId = model.NewId()
  	_, resp, err = apiClient.AuthorizeOAuthApp(authRequest)
  	require.Error(t, err)
  	CheckNotFoundStatus(t, resp)
  
  	// test callback URI doesn't have malformed query parameters
  	oappWithQueryParamInCallback := &model.OAuthApp{
  		Name:         GenerateTestAppName(),
  		Homepage:     "https://nowhere.com",
  		Description:  "test",
  		CallbackUrls: []string{"https://nowhere.com?simply=lovely"},
  		CreatorId:    th.SystemAdminUser.Id,
  	}
  
  	rapp, appErr = th.App.CreateOAuthApp(oappWithQueryParamInCallback)
  	require.Nil(t, appErr)
  
  	authRequest = &model.AuthorizeRequest{
  		ResponseType: model.AuthCodeResponseType,
  		ClientId:     rapp.Id,
  		RedirectURI:  rapp.CallbackUrls[0],
  		Scope:        "",
  		State:        "123",
  	}
  	uriResponse, _, err := apiClient.AuthorizeOAuthApp(authRequest)
  	require.NoError(t, err)
  	ru, _ = url.Parse(uriResponse)
  	require.NotEmpty(t, uriResponse, "redirect url should be set")
  	require.NotNil(t, ru, "redirect url unparseable")
  	// require no query parameter to have "?"
  	require.False(t, strings.Contains(ru.RawQuery, "?"), "should not malform query parameters")
  	require.NotEmpty(t, ru.Query().Get("code"), "authorization code not returned")
  	require.Equal(t, ru.Query().Get("state"), authRequest.State, "returned state doesn't match")
  }
  
  func TestDeauthorizeOAuthApp(t *testing.T) {
  	th := Setup(t).InitBasic()
  	th.Login(apiClient, th.SystemAdminUser)
  	defer th.TearDown()
  
  	enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider
  	defer func() {
  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth })
  	}()
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true })
  
  	oapp := &model.OAuthApp{
  		Name:         GenerateTestAppName(),
  		Homepage:     "https://nowhere.com",
  		Description:  "test",
  		CallbackUrls: []string{"https://nowhere.com"},
  		CreatorId:    th.SystemAdminUser.Id,
  	}
  
  	rapp, appErr := th.App.CreateOAuthApp(oapp)
  	require.Nil(t, appErr)
  
  	authRequest := &model.AuthorizeRequest{
  		ResponseType: model.AuthCodeResponseType,
  		ClientId:     rapp.Id,
  		RedirectURI:  rapp.CallbackUrls[0],
  		Scope:        "",
  		State:        "123",
  	}
  
  	_, _, err := apiClient.AuthorizeOAuthApp(authRequest)
  	require.NoError(t, err)
  
  	_, err = apiClient.DeauthorizeOAuthApp(rapp.Id)
  	require.NoError(t, err)
  
  	resp, err := apiClient.DeauthorizeOAuthApp("junk")
  	require.Error(t, err)
  	CheckBadRequestStatus(t, resp)
  
  	_, err = apiClient.DeauthorizeOAuthApp(model.NewId())
  	require.NoError(t, err)
  
  	th.Logout(apiClient)
  	resp, err = apiClient.DeauthorizeOAuthApp(rapp.Id)
  	require.Error(t, err)
  	CheckUnauthorizedStatus(t, resp)
  }
  
  func TestOAuthAccessToken(t *testing.T) {
  	if testing.Short() {
  		t.SkipNow()
  	}
  
  	th := Setup(t).InitBasic()
  	th.Login(apiClient, th.SystemAdminUser)
  	defer th.TearDown()
  
  	enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider
  	defer func() {
  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth })
  	}()
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true })
  
  	defaultRolePermissions := th.SaveDefaultRolePermissions()
  	defer func() {
  		th.RestoreDefaultRolePermissions(defaultRolePermissions)
  	}()
  	th.AddPermissionToRole(model.PermissionManageOAuth.Id, model.TeamUserRoleId)
  	th.AddPermissionToRole(model.PermissionManageOAuth.Id, model.SystemUserRoleId)
  
  	oauthApp := &model.OAuthApp{
  		Name:         "TestApp5" + model.NewId(),
  		Homepage:     "https://nowhere.com",
  		Description:  "test",
  		CallbackUrls: []string{"https://nowhere.com"},
  		CreatorId:    th.SystemAdminUser.Id,
  	}
  	oauthApp, appErr := th.App.CreateOAuthApp(oauthApp)
  	require.Nil(t, appErr)
  
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = false })
  	data := url.Values{"grant_type": []string{"junk"}, "client_id": []string{"12345678901234567890123456"}, "client_secret": []string{"12345678901234567890123456"}, "code": []string{"junk"}, "redirect_uri": []string{oauthApp.CallbackUrls[0]}}
  
  	_, _, err := apiClient.GetOAuthAccessToken(data)
  	require.Error(t, err, "should have failed - oauth providing turned off")
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true })
  
  	authRequest := &model.AuthorizeRequest{
  		ResponseType: model.AuthCodeResponseType,
  		ClientId:     oauthApp.Id,
  		RedirectURI:  oauthApp.CallbackUrls[0],
  		Scope:        "all",
  		State:        "123",
  	}
  
  	redirect, _, err := apiClient.AuthorizeOAuthApp(authRequest)
  	require.NoError(t, err)
  	rurl, _ := url.Parse(redirect)
  
  	apiClient.Logout()
  
  	data = url.Values{"grant_type": []string{"junk"}, "client_id": []string{oauthApp.Id}, "client_secret": []string{oauthApp.ClientSecret}, "code": []string{rurl.Query().Get("code")}, "redirect_uri": []string{oauthApp.CallbackUrls[0]}}
  
  	_, _, err = apiClient.GetOAuthAccessToken(data)
  	require.Error(t, err, "should have failed - bad grant type")
  
  	data.Set("grant_type", model.AccessTokenGrantType)
  	data.Set("client_id", "")
  	_, _, err = apiClient.GetOAuthAccessToken(data)
  	require.Error(t, err, "should have failed - missing client id")
  
  	data.Set("client_id", "junk")
  	_, _, err = apiClient.GetOAuthAccessToken(data)
  	require.Error(t, err, "should have failed - bad client id")
  
  	data.Set("client_id", oauthApp.Id)
  	data.Set("client_secret", "")
  	_, _, err = apiClient.GetOAuthAccessToken(data)
  	require.Error(t, err, "should have failed - missing client secret")
  
  	data.Set("client_secret", "junk")
  	_, _, err = apiClient.GetOAuthAccessToken(data)
  	require.Error(t, err, "should have failed - bad client secret")
  
  	data.Set("client_secret", oauthApp.ClientSecret)
  	data.Set("code", "")
  	_, _, err = apiClient.GetOAuthAccessToken(data)
  	require.Error(t, err, "should have failed - missing code")
  
  	data.Set("code", "junk")
  	_, _, err = apiClient.GetOAuthAccessToken(data)
  	require.Error(t, err, "should have failed - bad code")
  
  	data.Set("code", rurl.Query().Get("code"))
  	data.Set("redirect_uri", "junk")
  	_, _, err = apiClient.GetOAuthAccessToken(data)
  	require.Error(t, err, "should have failed - non-matching redirect uri")
  
  	// reset data for successful request
  	data.Set("grant_type", model.AccessTokenGrantType)
  	data.Set("client_id", oauthApp.Id)
  	data.Set("client_secret", oauthApp.ClientSecret)
  	data.Set("code", rurl.Query().Get("code"))
  	data.Set("redirect_uri", oauthApp.CallbackUrls[0])
  
  	token := ""
  	refreshToken := ""
  	rsp, _, err := apiClient.GetOAuthAccessToken(data)
  	require.NoError(t, err)
  	require.NotEmpty(t, rsp.AccessToken, "access token not returned")
  	require.NotEmpty(t, rsp.RefreshToken, "refresh token not returned")
  	token, refreshToken = rsp.AccessToken, rsp.RefreshToken
  	require.Equal(t, rsp.TokenType, model.AccessTokenType, "access token type incorrect")
  
  	_, err = apiClient.DoAPIGet("/oauth_test", "")
  	require.NoError(t, err)
  
  	apiClient.SetOAuthToken("")
  	_, err = apiClient.DoAPIGet("/oauth_test", "")
  	require.Error(t, err, "should have failed - no access token provided")
  
  	apiClient.SetOAuthToken("badtoken")
  	_, err = apiClient.DoAPIGet("/oauth_test", "")
  	require.Error(t, err, "should have failed - bad token provided")
  
  	apiClient.SetOAuthToken(token)
  	_, err = apiClient.DoAPIGet("/oauth_test", "")
  	require.NoError(t, err)
  
  	_, _, err = apiClient.GetOAuthAccessToken(data)
  	require.Error(t, err, "should have failed - tried to reuse auth code")
  
  	data.Set("grant_type", model.RefreshTokenGrantType)
  	data.Set("client_id", oauthApp.Id)
  	data.Set("client_secret", oauthApp.ClientSecret)
  	data.Set("refresh_token", "")
  	data.Set("redirect_uri", oauthApp.CallbackUrls[0])
  	data.Del("code")
  	_, _, err = apiClient.GetOAuthAccessToken(data)
  	require.Error(t, err, "Should have failed - refresh token empty")
  
  	data.Set("refresh_token", refreshToken)
  	rsp, _, err = apiClient.GetOAuthAccessToken(data)
  	require.NoError(t, err)
  	require.NotEmpty(t, rsp.AccessToken, "access token not returned")
  	require.NotEmpty(t, rsp.RefreshToken, "refresh token not returned")
  	require.NotEqual(t, rsp.RefreshToken, refreshToken, "refresh token did not update")
  	require.Equal(t, rsp.TokenType, model.AccessTokenType, "access token type incorrect")
  
  	apiClient.SetOAuthToken(rsp.AccessToken)
  	_, err = apiClient.DoAPIGet("/oauth_test", "")
  	require.NoError(t, err)
  
  	data.Set("refresh_token", rsp.RefreshToken)
  	rsp, _, err = apiClient.GetOAuthAccessToken(data)
  	require.NoError(t, err)
  	require.NotEmpty(t, rsp.AccessToken, "access token not returned")
  	require.NotEmpty(t, rsp.RefreshToken, "refresh token not returned")
  	require.NotEqual(t, rsp.RefreshToken, refreshToken, "refresh token did not update")
  	require.Equal(t, rsp.TokenType, model.AccessTokenType, "access token type incorrect")
  
  	apiClient.SetOAuthToken(rsp.AccessToken)
  	_, err = apiClient.DoAPIGet("/oauth_test", "")
  	require.NoError(t, err)
  
  	authData := &model.AuthData{ClientId: oauthApp.Id, RedirectUri: oauthApp.CallbackUrls[0], UserId: th.BasicUser.Id, Code: model.NewId(), ExpiresIn: -1}
  	_, err = th.App.Srv().Store().OAuth().SaveAuthData(authData)
  	require.NoError(t, err)
  
  	data.Set("grant_type", model.AccessTokenGrantType)
  	data.Set("client_id", oauthApp.Id)
  	data.Set("client_secret", oauthApp.ClientSecret)
  	data.Set("redirect_uri", oauthApp.CallbackUrls[0])
  	data.Set("code", authData.Code)
  	data.Del("refresh_token")
  	_, _, err = apiClient.GetOAuthAccessToken(data)
  	require.Error(t, err, "Should have failed - code is expired")
  
  	apiClient.ClearOAuthToken()
  }
  
  func TestMobileLoginWithOAuth(t *testing.T) {
  	th := Setup(t).InitBasic()
  	defer th.TearDown()
  	c := &Context{
  		App:        th.App,
  		AppContext: th.Context,
  		Params: &Params{
  			Service: "gitlab",
  		},
  	}
  
  	var siteURL = "http://localhost:8065"
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SiteURL = siteURL })
  
  	translationFunc := i18n.GetUserTranslations("en")
  	c.AppContext.SetT(translationFunc)
  	c.Logger = th.TestLogger
  	provider := &MattermostTestProvider{}
  	einterfaces.RegisterOAuthProvider(model.ServiceGitlab, provider)
  
  	t.Run("Should include redirect URL in the output when valid URL Scheme is passed", func(t *testing.T) {
  		responseWriter := httptest.NewRecorder()
  		request, _ := http.NewRequest(http.MethodGet, th.App.GetSiteURL()+"/oauth/gitlab/mobile_login?redirect_to="+url.QueryEscape("randomScheme://"), nil)
  		mobileLoginWithOAuth(c, responseWriter, request)
  		assert.Contains(t, responseWriter.Body.String(), "randomScheme://")
  		assert.NotContains(t, responseWriter.Body.String(), siteURL)
  	})
  
  	t.Run("Should not include the redirect URL consisting of javascript protocol", func(t *testing.T) {
  		responseWriter := httptest.NewRecorder()
  		request, _ := http.NewRequest(http.MethodGet, th.App.GetSiteURL()+"/oauth/gitlab/mobile_login?redirect_to="+url.QueryEscape("javascript:alert('hello')"), nil)
  		mobileLoginWithOAuth(c, responseWriter, request)
  		assert.NotContains(t, responseWriter.Body.String(), "javascript:alert('hello')")
  		assert.Contains(t, responseWriter.Body.String(), siteURL)
  	})
  
  	t.Run("Should not include the redirect URL consisting of javascript protocol in mixed case", func(t *testing.T) {
  		responseWriter := httptest.NewRecorder()
  		request, _ := http.NewRequest(http.MethodGet, th.App.GetSiteURL()+"/oauth/gitlab/mobile_login?redirect_to="+url.QueryEscape("JaVasCript:alert('hello')"), nil)
  		mobileLoginWithOAuth(c, responseWriter, request)
  		assert.NotContains(t, responseWriter.Body.String(), "JaVasCript:alert('hello')")
  		assert.Contains(t, responseWriter.Body.String(), siteURL)
  	})
  }
  
  func TestOAuthComplete(t *testing.T) {
  	if testing.Short() {
  		t.SkipNow()
  	}
  
  	th := Setup(t).InitBasic()
  	th.Login(apiClient, th.SystemAdminUser)
  	defer th.TearDown()
  
  	gitLabSettingsEnable := th.App.Config().GitLabSettings.Enable
  	gitLabSettingsAuthEndpoint := th.App.Config().GitLabSettings.AuthEndpoint
  	gitLabSettingsId := th.App.Config().GitLabSettings.Id
  	gitLabSettingsSecret := th.App.Config().GitLabSettings.Secret
  	gitLabSettingsTokenEndpoint := th.App.Config().GitLabSettings.TokenEndpoint
  	gitLabSettingsUserAPIEndpoint := th.App.Config().GitLabSettings.UserAPIEndpoint
  	enableOAuthServiceProvider := th.App.Config().ServiceSettings.EnableOAuthServiceProvider
  	defer func() {
  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.Enable = gitLabSettingsEnable })
  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.AuthEndpoint = gitLabSettingsAuthEndpoint })
  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.Id = gitLabSettingsId })
  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.Secret = gitLabSettingsSecret })
  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.TokenEndpoint = gitLabSettingsTokenEndpoint })
  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.UserAPIEndpoint = gitLabSettingsUserAPIEndpoint })
  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuthServiceProvider })
  	}()
  
  	r, err := HTTPGet(apiClient.URL+"/login/gitlab/complete?code=123", apiClient.HTTPClient, "", true)
  	assert.Error(t, err)
  	closeBody(r)
  
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.Enable = true })
  	r, err = HTTPGet(apiClient.URL+"/login/gitlab/complete?code=123&state=!#$#F@#Yˆ&~ñ", apiClient.HTTPClient, "", true)
  	assert.Error(t, err)
  	closeBody(r)
  
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.AuthEndpoint = apiClient.URL + "/oauth/authorize" })
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.Id = model.NewId() })
  
  	stateProps := map[string]string{}
  	stateProps["action"] = model.OAuthActionLogin
  	stateProps["team_id"] = th.BasicTeam.Id
  	stateProps["redirect_to"] = *th.App.Config().GitLabSettings.AuthEndpoint
  
  	state := base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(stateProps)))
  	r, err = HTTPGet(apiClient.URL+"/login/gitlab/complete?code=123&state="+url.QueryEscape(state), apiClient.HTTPClient, "", true)
  	assert.Error(t, err)
  	closeBody(r)
  
  	stateProps["hash"] = utils.HashSha256(*th.App.Config().GitLabSettings.Id)
  	state = base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(stateProps)))
  	r, err = HTTPGet(apiClient.URL+"/login/gitlab/complete?code=123&state="+url.QueryEscape(state), apiClient.HTTPClient, "", true)
  	assert.Error(t, err)
  	closeBody(r)
  
  	// We are going to use mattermost as the provider emulating gitlab
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true })
  
  	defaultRolePermissions := th.SaveDefaultRolePermissions()
  	defer func() {
  		th.RestoreDefaultRolePermissions(defaultRolePermissions)
  	}()
  	th.AddPermissionToRole(model.PermissionManageOAuth.Id, model.TeamUserRoleId)
  	th.AddPermissionToRole(model.PermissionManageOAuth.Id, model.SystemUserRoleId)
  
  	oauthApp := &model.OAuthApp{
  		Name:        "TestApp5" + model.NewId(),
  		Homepage:    "https://nowhere.com",
  		Description: "test",
  		CallbackUrls: []string{
  			apiClient.URL + "/signup/" + model.ServiceGitlab + "/complete",
  			apiClient.URL + "/login/" + model.ServiceGitlab + "/complete",
  		},
  		CreatorId: th.SystemAdminUser.Id,
  		IsTrusted: true,
  	}
  	oauthApp, appErr := th.App.CreateOAuthApp(oauthApp)
  	require.Nil(t, appErr)
  
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.Id = oauthApp.Id })
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.Secret = oauthApp.ClientSecret })
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.AuthEndpoint = apiClient.URL + "/oauth/authorize" })
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.TokenEndpoint = apiClient.URL + "/oauth/access_token" })
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.UserAPIEndpoint = apiClient.APIURL + "/users/me" })
  
  	provider := &MattermostTestProvider{}
  
  	authRequest := &model.AuthorizeRequest{
  		ResponseType: model.AuthCodeResponseType,
  		ClientId:     oauthApp.Id,
  		RedirectURI:  oauthApp.CallbackUrls[0],
  		Scope:        "all",
  		State:        "123",
  	}
  
  	redirect, _, err := apiClient.AuthorizeOAuthApp(authRequest)
  	require.NoError(t, err)
  	rurl, _ := url.Parse(redirect)
  
  	code := rurl.Query().Get("code")
  	stateProps["action"] = model.OAuthActionEmailToSSO
  	delete(stateProps, "team_id")
  	stateProps["redirect_to"] = *th.App.Config().GitLabSettings.AuthEndpoint
  	stateProps["hash"] = utils.HashSha256(*th.App.Config().GitLabSettings.Id)
  	stateProps["redirect_to"] = "/oauth/authorize"
  	state = base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(stateProps)))
  	r, err = HTTPGet(apiClient.URL+"/login/"+model.ServiceGitlab+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), apiClient.HTTPClient, "", false)
  	if err == nil {
  		closeBody(r)
  	}
  
  	einterfaces.RegisterOAuthProvider(model.ServiceGitlab, provider)
  
  	redirect, _, err = apiClient.AuthorizeOAuthApp(authRequest)
  	require.NoError(t, err)
  	rurl, _ = url.Parse(redirect)
  
  	code = rurl.Query().Get("code")
  	r, err = HTTPGet(apiClient.URL+"/login/"+model.ServiceGitlab+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), apiClient.HTTPClient, "", false)
  	if err == nil {
  		closeBody(r)
  	}
  
  	_, nErr := th.App.Srv().Store().User().UpdateAuthData(
  		th.BasicUser.Id, model.ServiceGitlab, &th.BasicUser.Email, th.BasicUser.Email, true)
  	require.NoError(t, nErr)
  
  	redirect, _, err = apiClient.AuthorizeOAuthApp(authRequest)
  	require.NoError(t, err)
  	rurl, _ = url.Parse(redirect)
  
  	code = rurl.Query().Get("code")
  	stateProps["action"] = model.OAuthActionLogin
  	state = base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(stateProps)))
  	if r, err = HTTPGet(apiClient.URL+"/login/"+model.ServiceGitlab+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), apiClient.HTTPClient, "", false); err == nil {
  		closeBody(r)
  	}
  
  	redirect, _, err = apiClient.AuthorizeOAuthApp(authRequest)
  	require.NoError(t, err)
  	rurl, _ = url.Parse(redirect)
  
  	code = rurl.Query().Get("code")
  	delete(stateProps, "action")
  	state = base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(stateProps)))
  	if r, err = HTTPGet(apiClient.URL+"/login/"+model.ServiceGitlab+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), apiClient.HTTPClient, "", false); err == nil {
  		closeBody(r)
  	}
  
  	redirect, _, err = apiClient.AuthorizeOAuthApp(authRequest)
  	require.NoError(t, err)
  	rurl, _ = url.Parse(redirect)
  
  	code = rurl.Query().Get("code")
  	stateProps["action"] = model.OAuthActionSignup
  	state = base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(stateProps)))
  	if r, err := HTTPGet(apiClient.URL+"/login/"+model.ServiceGitlab+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), apiClient.HTTPClient, "", false); err == nil {
  		closeBody(r)
  	}
  }
  
  func TestOAuthComplete_ErrorMessages(t *testing.T) {
  	th := Setup(t).InitBasic()
  	defer th.TearDown()
  	c := &Context{
  		App:        th.App,
  		AppContext: th.Context,
  		Params: &Params{
  			Service: "gitlab",
  		},
  	}
  
  	translationFunc := i18n.GetUserTranslations("en")
  	c.AppContext.SetT(translationFunc)
  	c.Logger = mlog.CreateConsoleTestLogger(true, mlog.LvlDebug)
  	defer c.Logger.Shutdown()
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.Enable = true })
  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true })
  	provider := &MattermostTestProvider{}
  	einterfaces.RegisterOAuthProvider(model.ServiceGitlab, provider)
  
  	responseWriter := httptest.NewRecorder()
  
  	// Renders for web & mobile app with webview
  	request, _ := http.NewRequest(http.MethodGet, th.App.GetSiteURL()+"/signup/gitlab/complete?code=1234", nil)
  
  	completeOAuth(c, responseWriter, request)
  	assert.Contains(t, responseWriter.Body.String(), "<!-- web error message -->")
  
  	// Renders for mobile app with redirect url
  	stateProps := map[string]string{}
  	stateProps["action"] = model.OAuthActionMobile
  	stateProps["redirect_to"] = th.App.Config().NativeAppSettings.AppCustomURLSchemes[0]
  	state := base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(stateProps)))
  	request2, _ := http.NewRequest(http.MethodGet, th.App.GetSiteURL()+"/signup/gitlab/complete?code=1234&state="+url.QueryEscape(state), nil)
  
  	completeOAuth(c, responseWriter, request2)
  	assert.Contains(t, responseWriter.Body.String(), "<!-- mobile app message -->")
  }
  
  func HTTPGet(url string, httpClient *http.Client, authToken string, followRedirect bool) (*http.Response, error) {
  	rq, _ := http.NewRequest("GET", url, nil)
  	rq.Close = true
  
  	if authToken != "" {
  		rq.Header.Set(model.HeaderAuth, authToken)
  	}
  
  	if !followRedirect {
  		httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
  			return http.ErrUseLastResponse
  		}
  	}
  
  	if rp, err := httpClient.Do(rq); err != nil {
  		return nil, err
  	} else if rp.StatusCode == 304 {
  		return rp, nil
  	} else if rp.StatusCode == 307 {
  		return rp, nil
  	} else if rp.StatusCode >= 300 {
  		defer closeBody(rp)
  		return rp, model.AppErrorFromJSON(rp.Body)
  	} else {
  		return rp, nil
  	}
  }
  
  func closeBody(r *http.Response) {
  	if r != nil && r.Body != nil {
  		io.ReadAll(r.Body)
  		r.Body.Close()
  	}
  }
  
  type MattermostTestProvider struct {
  }
  
  func (m *MattermostTestProvider) GetUserFromJSON(data io.Reader, tokenUser *model.User) (*model.User, error) {
  	var user model.User
  	if err := json.NewDecoder(data).Decode(&user); err != nil {
  		return nil, err
  	}
  	user.AuthData = &user.Email
  	return &user, nil
  }
  
  func (m *MattermostTestProvider) GetSSOSettings(config *model.Config, service string) (*model.SSOSettings, error) {
  	return &config.GitLabSettings, nil
  }
  
  func (m *MattermostTestProvider) GetUserFromIdToken(token string) (*model.User, error) {
  	return nil, nil
  }
  
  func (m *MattermostTestProvider) IsSameUser(dbUser, oauthUser *model.User) bool {
  	return dbUser.AuthData == oauthUser.AuthData
  }
  
  func GenerateTestAppName() string {
  	return "fakeoauthapp" + model.NewRandomString(10)
  }
  
  func checkHTTPStatus(t *testing.T, resp *model.Response, expectedStatus int) {
  	t.Helper()
  
  	require.NotNilf(t, resp, "Unexpected nil response, expected http status:%v", expectedStatus)
  
  	require.Equalf(t, expectedStatus, resp.StatusCode, "Expected http status:%v, got %v", expectedStatus, resp.StatusCode)
  }
  
  func CheckForbiddenStatus(t *testing.T, resp *model.Response) {
  	t.Helper()
  	checkHTTPStatus(t, resp, http.StatusForbidden)
  }
  
  func CheckUnauthorizedStatus(t *testing.T, resp *model.Response) {
  	t.Helper()
  	checkHTTPStatus(t, resp, http.StatusUnauthorized)
  }
  
  func CheckNotFoundStatus(t *testing.T, resp *model.Response) {
  	t.Helper()
  	checkHTTPStatus(t, resp, http.StatusNotFound)
  }
  
  func CheckBadRequestStatus(t *testing.T, resp *model.Response) {
  	t.Helper()
  	checkHTTPStatus(t, resp, http.StatusBadRequest)
  }
  
  func (th *TestHelper) Login(client *model.Client4, user *model.User) {
  	session := &model.Session{
  		UserId:  user.Id,
  		Roles:   user.GetRawRoles(),
  		IsOAuth: false,
  	}
  	session, _ = th.App.CreateSession(session)
  	client.AuthToken = session.Token
  	client.AuthType = model.HeaderBearer
  }
  
  func (th *TestHelper) Logout(client *model.Client4) {
  	client.AuthToken = ""
  }
  
  func (th *TestHelper) SaveDefaultRolePermissions() map[string][]string {
  	results := make(map[string][]string)
  
  	for _, roleName := range []string{
  		"system_user",
  		"system_admin",
  		"team_user",
  		"team_admin",
  		"channel_user",
  		"channel_admin",
  	} {
  		role, err1 := th.App.GetRoleByName(context.Background(), roleName)
  		if err1 != nil {
  			panic(err1)
  		}
  
  		results[roleName] = role.Permissions
  	}
  	return results
  }
  
  func (th *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) {
  	for roleName, permissions := range data {
  		role, err1 := th.App.GetRoleByName(context.Background(), roleName)
  		if err1 != nil {
  			panic(err1)
  		}
  
  		if strings.Join(role.Permissions, " ") == strings.Join(permissions, " ") {
  			continue
  		}
  
  		role.Permissions = permissions
  
  		_, err2 := th.App.UpdateRole(role)
  		if err2 != nil {
  			panic(err2)
  		}
  	}
  }
  
  // func (th *TestHelper) RemovePermissionFromRole(permission string, roleName string) {
  // 	utils.DisableDebugLogForTest()
  
  // 	role, err1 := th.App.GetRoleByName(roleName)
  // 	if err1 != nil {
  // 		utils.EnableDebugLogForTest()
  // 		panic(err1)
  // 	}
  
  // 	var newPermissions []string
  // 	for _, p := range role.Permissions {
  // 		if p != permission {
  // 			newPermissions = append(newPermissions, p)
  // 		}
  // 	}
  
  // 	if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") {
  // 		utils.EnableDebugLogForTest()
  // 		return
  // 	}
  
  // 	role.Permissions = newPermissions
  
  // 	_, err2 := th.App.UpdateRole(role)
  // 	if err2 != nil {
  // 		utils.EnableDebugLogForTest()
  // 		panic(err2)
  // 	}
  
  // 	utils.EnableDebugLogForTest()
  // }
  
  func (th *TestHelper) AddPermissionToRole(permission string, roleName string) {
  	role, err1 := th.App.GetRoleByName(context.Background(), roleName)
  	if err1 != nil {
  		panic(err1)
  	}
  
  	for _, existingPermission := range role.Permissions {
  		if existingPermission == permission {
  			return
  		}
  	}
  
  	role.Permissions = append(role.Permissions, permission)
  
  	_, err2 := th.App.UpdateRole(role)
  	if err2 != nil {
  		panic(err2)
  	}
  }
  
  func TestFullyQualifiedRedirectURL(t *testing.T) {
  	const siteURL = "https://xxx.yyy/mm"
  	for target, expected := range map[string]string{
  		"":            "https://xxx.yyy/mm",
  		"/":           "https://xxx.yyy/mm/",
  		"some-path":   "https://xxx.yyy/mm/some-path",
  		"/some-path":  "https://xxx.yyy/mm/some-path",
  		"/some-path/": "https://xxx.yyy/mm/some-path/",
  	} {
  		t.Run(target, func(t *testing.T) {
  			require.Equal(t, expected, fullyQualifiedRedirectURL(siteURL, target))
  		})
  	}
  }
  