github.com/nhannv/mattermost-server@v5.11.1+incompatible/app/oauth.go (about)

     1  // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"bytes"
     8  	b64 "encoding/base64"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"net/http"
    13  	"net/url"
    14  	"strconv"
    15  	"strings"
    16  	"time"
    17  
    18  	"github.com/mattermost/mattermost-server/einterfaces"
    19  	"github.com/mattermost/mattermost-server/mlog"
    20  	"github.com/mattermost/mattermost-server/model"
    21  	"github.com/mattermost/mattermost-server/store"
    22  	"github.com/mattermost/mattermost-server/utils"
    23  )
    24  
    25  const (
    26  	OAUTH_COOKIE_MAX_AGE_SECONDS = 30 * 60 // 30 minutes
    27  	COOKIE_OAUTH                 = "MMOAUTH"
    28  )
    29  
    30  func (a *App) CreateOAuthApp(app *model.OAuthApp) (*model.OAuthApp, *model.AppError) {
    31  	if !*a.Config().ServiceSettings.EnableOAuthServiceProvider {
    32  		return nil, model.NewAppError("CreateOAuthApp", "api.oauth.register_oauth_app.turn_off.app_error", nil, "", http.StatusNotImplemented)
    33  	}
    34  
    35  	app.ClientSecret = model.NewId()
    36  
    37  	result := <-a.Srv.Store.OAuth().SaveApp(app)
    38  	if result.Err != nil {
    39  		return nil, result.Err
    40  	}
    41  
    42  	return result.Data.(*model.OAuthApp), nil
    43  }
    44  
    45  func (a *App) GetOAuthApp(appId string) (*model.OAuthApp, *model.AppError) {
    46  	if !*a.Config().ServiceSettings.EnableOAuthServiceProvider {
    47  		return nil, model.NewAppError("GetOAuthApp", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
    48  	}
    49  
    50  	result := <-a.Srv.Store.OAuth().GetApp(appId)
    51  	if result.Err != nil {
    52  		return nil, result.Err
    53  	}
    54  
    55  	return result.Data.(*model.OAuthApp), nil
    56  }
    57  
    58  func (a *App) UpdateOauthApp(oldApp, updatedApp *model.OAuthApp) (*model.OAuthApp, *model.AppError) {
    59  	if !*a.Config().ServiceSettings.EnableOAuthServiceProvider {
    60  		return nil, model.NewAppError("UpdateOauthApp", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
    61  	}
    62  
    63  	updatedApp.Id = oldApp.Id
    64  	updatedApp.CreatorId = oldApp.CreatorId
    65  	updatedApp.CreateAt = oldApp.CreateAt
    66  	updatedApp.ClientSecret = oldApp.ClientSecret
    67  
    68  	result := <-a.Srv.Store.OAuth().UpdateApp(updatedApp)
    69  	if result.Err != nil {
    70  		return nil, result.Err
    71  	}
    72  
    73  	return result.Data.([2]*model.OAuthApp)[0], nil
    74  }
    75  
    76  func (a *App) DeleteOAuthApp(appId string) *model.AppError {
    77  	if !*a.Config().ServiceSettings.EnableOAuthServiceProvider {
    78  		return model.NewAppError("DeleteOAuthApp", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
    79  	}
    80  
    81  	if err := (<-a.Srv.Store.OAuth().DeleteApp(appId)).Err; err != nil {
    82  		return err
    83  	}
    84  
    85  	if err := a.InvalidateAllCaches(); err != nil {
    86  		mlog.Error(err.Error())
    87  	}
    88  
    89  	return nil
    90  }
    91  
    92  func (a *App) GetOAuthApps(page, perPage int) ([]*model.OAuthApp, *model.AppError) {
    93  	if !*a.Config().ServiceSettings.EnableOAuthServiceProvider {
    94  		return nil, model.NewAppError("GetOAuthApps", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
    95  	}
    96  
    97  	result := <-a.Srv.Store.OAuth().GetApps(page*perPage, perPage)
    98  	if result.Err != nil {
    99  		return nil, result.Err
   100  	}
   101  
   102  	return result.Data.([]*model.OAuthApp), nil
   103  }
   104  
   105  func (a *App) GetOAuthAppsByCreator(userId string, page, perPage int) ([]*model.OAuthApp, *model.AppError) {
   106  	if !*a.Config().ServiceSettings.EnableOAuthServiceProvider {
   107  		return nil, model.NewAppError("GetOAuthAppsByUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
   108  	}
   109  
   110  	result := <-a.Srv.Store.OAuth().GetAppByUser(userId, page*perPage, perPage)
   111  	if result.Err != nil {
   112  		return nil, result.Err
   113  	}
   114  
   115  	return result.Data.([]*model.OAuthApp), nil
   116  }
   117  
   118  func (a *App) GetOAuthImplicitRedirect(userId string, authRequest *model.AuthorizeRequest) (string, *model.AppError) {
   119  	session, err := a.GetOAuthAccessTokenForImplicitFlow(userId, authRequest)
   120  	if err != nil {
   121  		return "", err
   122  	}
   123  
   124  	values := &url.Values{}
   125  	values.Add("access_token", session.Token)
   126  	values.Add("token_type", "bearer")
   127  	values.Add("expires_in", strconv.FormatInt((session.ExpiresAt-model.GetMillis())/1000, 10))
   128  	values.Add("scope", authRequest.Scope)
   129  	values.Add("state", authRequest.State)
   130  
   131  	return fmt.Sprintf("%s#%s", authRequest.RedirectUri, values.Encode()), nil
   132  }
   133  
   134  func (a *App) GetOAuthCodeRedirect(userId string, authRequest *model.AuthorizeRequest) (string, *model.AppError) {
   135  	authData := &model.AuthData{UserId: userId, ClientId: authRequest.ClientId, CreateAt: model.GetMillis(), RedirectUri: authRequest.RedirectUri, State: authRequest.State, Scope: authRequest.Scope}
   136  	authData.Code = model.NewId() + model.NewId()
   137  
   138  	if result := <-a.Srv.Store.OAuth().SaveAuthData(authData); result.Err != nil {
   139  		return authRequest.RedirectUri + "?error=server_error&state=" + authRequest.State, nil
   140  	}
   141  
   142  	return authRequest.RedirectUri + "?code=" + url.QueryEscape(authData.Code) + "&state=" + url.QueryEscape(authData.State), nil
   143  }
   144  
   145  func (a *App) AllowOAuthAppAccessToUser(userId string, authRequest *model.AuthorizeRequest) (string, *model.AppError) {
   146  	if !*a.Config().ServiceSettings.EnableOAuthServiceProvider {
   147  		return "", model.NewAppError("AllowOAuthAppAccessToUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
   148  	}
   149  
   150  	if len(authRequest.Scope) == 0 {
   151  		authRequest.Scope = model.DEFAULT_SCOPE
   152  	}
   153  
   154  	result := <-a.Srv.Store.OAuth().GetApp(authRequest.ClientId)
   155  	if result.Err != nil {
   156  		return "", result.Err
   157  	}
   158  	oauthApp := result.Data.(*model.OAuthApp)
   159  
   160  	if !oauthApp.IsValidRedirectURL(authRequest.RedirectUri) {
   161  		return "", model.NewAppError("AllowOAuthAppAccessToUser", "api.oauth.allow_oauth.redirect_callback.app_error", nil, "", http.StatusBadRequest)
   162  	}
   163  
   164  	var redirectURI string
   165  	var err *model.AppError
   166  
   167  	switch authRequest.ResponseType {
   168  	case model.AUTHCODE_RESPONSE_TYPE:
   169  		redirectURI, err = a.GetOAuthCodeRedirect(userId, authRequest)
   170  	case model.IMPLICIT_RESPONSE_TYPE:
   171  		redirectURI, err = a.GetOAuthImplicitRedirect(userId, authRequest)
   172  	default:
   173  		return authRequest.RedirectUri + "?error=unsupported_response_type&state=" + authRequest.State, nil
   174  	}
   175  
   176  	if err != nil {
   177  		mlog.Error(err.Error())
   178  		return authRequest.RedirectUri + "?error=server_error&state=" + authRequest.State, nil
   179  	}
   180  
   181  	// This saves the OAuth2 app as authorized
   182  	authorizedApp := model.Preference{
   183  		UserId:   userId,
   184  		Category: model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP,
   185  		Name:     authRequest.ClientId,
   186  		Value:    authRequest.Scope,
   187  	}
   188  
   189  	if result = <-a.Srv.Store.Preference().Save(&model.Preferences{authorizedApp}); result.Err != nil {
   190  		mlog.Error(result.Err.Error())
   191  		return authRequest.RedirectUri + "?error=server_error&state=" + authRequest.State, nil
   192  	}
   193  
   194  	return redirectURI, nil
   195  }
   196  
   197  func (a *App) GetOAuthAccessTokenForImplicitFlow(userId string, authRequest *model.AuthorizeRequest) (*model.Session, *model.AppError) {
   198  	if !*a.Config().ServiceSettings.EnableOAuthServiceProvider {
   199  		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.disabled.app_error", nil, "", http.StatusNotImplemented)
   200  	}
   201  
   202  	oauthApp, err := a.GetOAuthApp(authRequest.ClientId)
   203  	if err != nil {
   204  		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.credentials.app_error", nil, "", http.StatusNotFound)
   205  	}
   206  
   207  	user, err := a.GetUser(userId)
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  
   212  	session, err := a.newSession(oauthApp.Name, user)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	accessData := &model.AccessData{ClientId: authRequest.ClientId, UserId: user.Id, Token: session.Token, RefreshToken: "", RedirectUri: authRequest.RedirectUri, ExpiresAt: session.ExpiresAt, Scope: authRequest.Scope}
   218  
   219  	if result := <-a.Srv.Store.OAuth().SaveAccessData(accessData); result.Err != nil {
   220  		mlog.Error(fmt.Sprint(result.Err))
   221  		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_saving.app_error", nil, "", http.StatusInternalServerError)
   222  	}
   223  
   224  	return session, nil
   225  }
   226  
   227  func (a *App) GetOAuthAccessTokenForCodeFlow(clientId, grantType, redirectUri, code, secret, refreshToken string) (*model.AccessResponse, *model.AppError) {
   228  	if !*a.Config().ServiceSettings.EnableOAuthServiceProvider {
   229  		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.disabled.app_error", nil, "", http.StatusNotImplemented)
   230  	}
   231  
   232  	result := <-a.Srv.Store.OAuth().GetApp(clientId)
   233  	if result.Err != nil {
   234  		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.credentials.app_error", nil, "", http.StatusNotFound)
   235  	}
   236  	oauthApp := result.Data.(*model.OAuthApp)
   237  
   238  	if oauthApp.ClientSecret != secret {
   239  		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.credentials.app_error", nil, "", http.StatusForbidden)
   240  	}
   241  
   242  	var user *model.User
   243  	var accessData *model.AccessData
   244  	var accessRsp *model.AccessResponse
   245  	if grantType == model.ACCESS_TOKEN_GRANT_TYPE {
   246  		var authData *model.AuthData
   247  		result := <-a.Srv.Store.OAuth().GetAuthData(code)
   248  		if result.Err != nil {
   249  			return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusInternalServerError)
   250  		}
   251  		authData = result.Data.(*model.AuthData)
   252  
   253  		if authData.IsExpired() {
   254  			<-a.Srv.Store.OAuth().RemoveAuthData(authData.Code)
   255  			return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusForbidden)
   256  		}
   257  
   258  		if authData.RedirectUri != redirectUri {
   259  			return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.redirect_uri.app_error", nil, "", http.StatusBadRequest)
   260  		}
   261  
   262  		result = <-a.Srv.Store.User().Get(authData.UserId)
   263  		if result.Err != nil {
   264  			return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_user.app_error", nil, "", http.StatusNotFound)
   265  		}
   266  		user = result.Data.(*model.User)
   267  
   268  		result = <-a.Srv.Store.OAuth().GetPreviousAccessData(user.Id, clientId)
   269  		if result.Err != nil {
   270  			return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal.app_error", nil, "", http.StatusInternalServerError)
   271  		}
   272  
   273  		if result.Data != nil {
   274  			accessData := result.Data.(*model.AccessData)
   275  			if accessData.IsExpired() {
   276  				access, err := a.newSessionUpdateToken(oauthApp.Name, accessData, user)
   277  				if err != nil {
   278  					return nil, err
   279  				}
   280  				accessRsp = access
   281  			} else {
   282  				// Return the same token and no need to create a new session
   283  				accessRsp = &model.AccessResponse{
   284  					AccessToken:  accessData.Token,
   285  					TokenType:    model.ACCESS_TOKEN_TYPE,
   286  					RefreshToken: accessData.RefreshToken,
   287  					ExpiresIn:    int32((accessData.ExpiresAt - model.GetMillis()) / 1000),
   288  				}
   289  			}
   290  		} else {
   291  			// Create a new session and return new access token
   292  			session, err := a.newSession(oauthApp.Name, user)
   293  			if err != nil {
   294  				return nil, err
   295  			}
   296  
   297  			accessData = &model.AccessData{ClientId: clientId, UserId: user.Id, Token: session.Token, RefreshToken: model.NewId(), RedirectUri: redirectUri, ExpiresAt: session.ExpiresAt, Scope: authData.Scope}
   298  
   299  			if result := <-a.Srv.Store.OAuth().SaveAccessData(accessData); result.Err != nil {
   300  				mlog.Error(fmt.Sprint(result.Err))
   301  				return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_saving.app_error", nil, "", http.StatusInternalServerError)
   302  			}
   303  
   304  			accessRsp = &model.AccessResponse{
   305  				AccessToken:  session.Token,
   306  				TokenType:    model.ACCESS_TOKEN_TYPE,
   307  				RefreshToken: accessData.RefreshToken,
   308  				ExpiresIn:    int32(*a.Config().ServiceSettings.SessionLengthSSOInDays * 60 * 60 * 24),
   309  			}
   310  		}
   311  
   312  		<-a.Srv.Store.OAuth().RemoveAuthData(authData.Code)
   313  	} else {
   314  		// When grantType is refresh_token
   315  		result := <-a.Srv.Store.OAuth().GetAccessDataByRefreshToken(refreshToken)
   316  		if result.Err != nil {
   317  			return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.refresh_token.app_error", nil, "", http.StatusNotFound)
   318  		}
   319  		accessData = result.Data.(*model.AccessData)
   320  
   321  		result = <-a.Srv.Store.User().Get(accessData.UserId)
   322  		if result.Err != nil {
   323  			return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_user.app_error", nil, "", http.StatusNotFound)
   324  		}
   325  		user = result.Data.(*model.User)
   326  
   327  		access, err := a.newSessionUpdateToken(oauthApp.Name, accessData, user)
   328  		if err != nil {
   329  			return nil, err
   330  		}
   331  		accessRsp = access
   332  	}
   333  
   334  	return accessRsp, nil
   335  }
   336  
   337  func (a *App) newSession(appName string, user *model.User) (*model.Session, *model.AppError) {
   338  	// Set new token an session
   339  	session := &model.Session{UserId: user.Id, Roles: user.Roles, IsOAuth: true}
   340  	session.GenerateCSRF()
   341  	session.SetExpireInDays(*a.Config().ServiceSettings.SessionLengthSSOInDays)
   342  	session.AddProp(model.SESSION_PROP_PLATFORM, appName)
   343  	session.AddProp(model.SESSION_PROP_OS, "OAuth2")
   344  	session.AddProp(model.SESSION_PROP_BROWSER, "OAuth2")
   345  
   346  	result := <-a.Srv.Store.Session().Save(session)
   347  	if result.Err != nil {
   348  		return nil, model.NewAppError("newSession", "api.oauth.get_access_token.internal_session.app_error", nil, "", http.StatusInternalServerError)
   349  	}
   350  	session = result.Data.(*model.Session)
   351  
   352  	a.AddSessionToCache(session)
   353  
   354  	return session, nil
   355  }
   356  
   357  func (a *App) newSessionUpdateToken(appName string, accessData *model.AccessData, user *model.User) (*model.AccessResponse, *model.AppError) {
   358  	// Remove the previous session
   359  	<-a.Srv.Store.Session().Remove(accessData.Token)
   360  
   361  	session, err := a.newSession(appName, user)
   362  	if err != nil {
   363  		return nil, err
   364  	}
   365  
   366  	accessData.Token = session.Token
   367  	accessData.RefreshToken = model.NewId()
   368  	accessData.ExpiresAt = session.ExpiresAt
   369  
   370  	if result := <-a.Srv.Store.OAuth().UpdateAccessData(accessData); result.Err != nil {
   371  		mlog.Error(fmt.Sprint(result.Err))
   372  		return nil, model.NewAppError("newSessionUpdateToken", "web.get_access_token.internal_saving.app_error", nil, "", http.StatusInternalServerError)
   373  	}
   374  	accessRsp := &model.AccessResponse{
   375  		AccessToken:  session.Token,
   376  		RefreshToken: accessData.RefreshToken,
   377  		TokenType:    model.ACCESS_TOKEN_TYPE,
   378  		ExpiresIn:    int32(*a.Config().ServiceSettings.SessionLengthSSOInDays * 60 * 60 * 24),
   379  	}
   380  
   381  	return accessRsp, nil
   382  }
   383  
   384  func (a *App) GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, service, teamId, action, redirectTo, loginHint string) (string, *model.AppError) {
   385  	stateProps := map[string]string{}
   386  	stateProps["action"] = action
   387  	if len(teamId) != 0 {
   388  		stateProps["team_id"] = teamId
   389  	}
   390  
   391  	if len(redirectTo) != 0 {
   392  		stateProps["redirect_to"] = redirectTo
   393  	}
   394  
   395  	authUrl, err := a.GetAuthorizationCode(w, r, service, stateProps, loginHint)
   396  	if err != nil {
   397  		return "", err
   398  	}
   399  
   400  	return authUrl, nil
   401  }
   402  
   403  func (a *App) GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service, teamId string) (string, *model.AppError) {
   404  	stateProps := map[string]string{}
   405  	stateProps["action"] = model.OAUTH_ACTION_SIGNUP
   406  	if len(teamId) != 0 {
   407  		stateProps["team_id"] = teamId
   408  	}
   409  
   410  	authUrl, err := a.GetAuthorizationCode(w, r, service, stateProps, "")
   411  	if err != nil {
   412  		return "", err
   413  	}
   414  
   415  	return authUrl, nil
   416  }
   417  
   418  func (a *App) GetAuthorizedAppsForUser(userId string, page, perPage int) ([]*model.OAuthApp, *model.AppError) {
   419  	if !*a.Config().ServiceSettings.EnableOAuthServiceProvider {
   420  		return nil, model.NewAppError("GetAuthorizedAppsForUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
   421  	}
   422  
   423  	result := <-a.Srv.Store.OAuth().GetAuthorizedApps(userId, page*perPage, perPage)
   424  	if result.Err != nil {
   425  		return nil, result.Err
   426  	}
   427  	apps := result.Data.([]*model.OAuthApp)
   428  
   429  	for k, a := range apps {
   430  		a.Sanitize()
   431  		apps[k] = a
   432  	}
   433  
   434  	return apps, nil
   435  }
   436  
   437  func (a *App) DeauthorizeOAuthAppForUser(userId, appId string) *model.AppError {
   438  	if !*a.Config().ServiceSettings.EnableOAuthServiceProvider {
   439  		return model.NewAppError("DeauthorizeOAuthAppForUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
   440  	}
   441  
   442  	// Revoke app sessions
   443  	result := <-a.Srv.Store.OAuth().GetAccessDataByUserForApp(userId, appId)
   444  	if result.Err != nil {
   445  		return result.Err
   446  	}
   447  	accessData := result.Data.([]*model.AccessData)
   448  
   449  	for _, ad := range accessData {
   450  		if err := a.RevokeAccessToken(ad.Token); err != nil {
   451  			return err
   452  		}
   453  
   454  		if rad := <-a.Srv.Store.OAuth().RemoveAccessData(ad.Token); rad.Err != nil {
   455  			return rad.Err
   456  		}
   457  	}
   458  
   459  	// Deauthorize the app
   460  	if err := (<-a.Srv.Store.Preference().Delete(userId, model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP, appId)).Err; err != nil {
   461  		return err
   462  	}
   463  
   464  	return nil
   465  }
   466  
   467  func (a *App) RegenerateOAuthAppSecret(app *model.OAuthApp) (*model.OAuthApp, *model.AppError) {
   468  	if !*a.Config().ServiceSettings.EnableOAuthServiceProvider {
   469  		return nil, model.NewAppError("RegenerateOAuthAppSecret", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
   470  	}
   471  
   472  	app.ClientSecret = model.NewId()
   473  	if update := <-a.Srv.Store.OAuth().UpdateApp(app); update.Err != nil {
   474  		return nil, update.Err
   475  	}
   476  
   477  	return app, nil
   478  }
   479  
   480  func (a *App) RevokeAccessToken(token string) *model.AppError {
   481  	session, _ := a.GetSession(token)
   482  	schan := a.Srv.Store.Session().Remove(token)
   483  
   484  	if result := <-a.Srv.Store.OAuth().GetAccessData(token); result.Err != nil {
   485  		return model.NewAppError("RevokeAccessToken", "api.oauth.revoke_access_token.get.app_error", nil, "", http.StatusBadRequest)
   486  	}
   487  
   488  	if result := <-a.Srv.Store.OAuth().RemoveAccessData(token); result.Err != nil {
   489  		return model.NewAppError("RevokeAccessToken", "api.oauth.revoke_access_token.del_token.app_error", nil, "", http.StatusInternalServerError)
   490  	}
   491  
   492  	if result := <-schan; result.Err != nil {
   493  		return model.NewAppError("RevokeAccessToken", "api.oauth.revoke_access_token.del_session.app_error", nil, "", http.StatusInternalServerError)
   494  	}
   495  
   496  	if session != nil {
   497  		a.ClearSessionCacheForUser(session.UserId)
   498  	}
   499  
   500  	return nil
   501  }
   502  
   503  func (a *App) CompleteOAuth(service string, body io.ReadCloser, teamId string, props map[string]string) (*model.User, *model.AppError) {
   504  	defer body.Close()
   505  
   506  	action := props["action"]
   507  
   508  	switch action {
   509  	case model.OAUTH_ACTION_SIGNUP:
   510  		return a.CreateOAuthUser(service, body, teamId)
   511  	case model.OAUTH_ACTION_LOGIN:
   512  		return a.LoginByOAuth(service, body, teamId)
   513  	case model.OAUTH_ACTION_EMAIL_TO_SSO:
   514  		return a.CompleteSwitchWithOAuth(service, body, props["email"])
   515  	case model.OAUTH_ACTION_SSO_TO_EMAIL:
   516  		return a.LoginByOAuth(service, body, teamId)
   517  	default:
   518  		return a.LoginByOAuth(service, body, teamId)
   519  	}
   520  }
   521  
   522  func (a *App) LoginByOAuth(service string, userData io.Reader, teamId string) (*model.User, *model.AppError) {
   523  	provider := einterfaces.GetOauthProvider(service)
   524  	if provider == nil {
   525  		return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.not_available.app_error",
   526  			map[string]interface{}{"Service": strings.Title(service)}, "", http.StatusNotImplemented)
   527  	}
   528  
   529  	buf := bytes.Buffer{}
   530  	if _, err := buf.ReadFrom(userData); err != nil {
   531  		return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.parse.app_error",
   532  			map[string]interface{}{"Service": service}, "", http.StatusBadRequest)
   533  	}
   534  	authUser := provider.GetUserFromJson(bytes.NewReader(buf.Bytes()))
   535  
   536  	authData := ""
   537  	if authUser.AuthData != nil {
   538  		authData = *authUser.AuthData
   539  	}
   540  
   541  	if len(authData) == 0 {
   542  		return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.parse.app_error",
   543  			map[string]interface{}{"Service": service}, "", http.StatusBadRequest)
   544  	}
   545  
   546  	user, err := a.GetUserByAuth(&authData, service)
   547  	if err != nil {
   548  		if err.Id == store.MISSING_AUTH_ACCOUNT_ERROR {
   549  			user, err = a.CreateOAuthUser(service, bytes.NewReader(buf.Bytes()), teamId)
   550  		} else {
   551  			return nil, err
   552  		}
   553  	} else {
   554  		// OAuth doesn't run through CheckUserPreflightAuthenticationCriteria, so prevent bot login
   555  		// here manually. Technically, the auth data above will fail to match a bot in the first
   556  		// place, but explicit is always better.
   557  		if user.IsBot {
   558  			return nil, model.NewAppError("loginByOAuth", "api.user.login_by_oauth.bot_login_forbidden.app_error", nil, "", http.StatusForbidden)
   559  		}
   560  
   561  		if err = a.UpdateOAuthUserAttrs(bytes.NewReader(buf.Bytes()), user, provider, service); err != nil {
   562  			return nil, err
   563  		}
   564  		if len(teamId) > 0 {
   565  			err = a.AddUserToTeamByTeamId(teamId, user)
   566  		}
   567  	}
   568  
   569  	if err != nil {
   570  		return nil, err
   571  	}
   572  
   573  	return user, nil
   574  }
   575  
   576  func (a *App) CompleteSwitchWithOAuth(service string, userData io.Reader, email string) (*model.User, *model.AppError) {
   577  	provider := einterfaces.GetOauthProvider(service)
   578  	if provider == nil {
   579  		return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.unavailable.app_error",
   580  			map[string]interface{}{"Service": strings.Title(service)}, "", http.StatusNotImplemented)
   581  	}
   582  	ssoUser := provider.GetUserFromJson(userData)
   583  	ssoEmail := ssoUser.Email
   584  
   585  	authData := ""
   586  	if ssoUser.AuthData != nil {
   587  		authData = *ssoUser.AuthData
   588  	}
   589  
   590  	if len(authData) == 0 {
   591  		return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.parse.app_error",
   592  			map[string]interface{}{"Service": service}, "", http.StatusBadRequest)
   593  	}
   594  
   595  	if len(email) == 0 {
   596  		return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.blank_email.app_error", nil, "", http.StatusBadRequest)
   597  	}
   598  
   599  	result := <-a.Srv.Store.User().GetByEmail(email)
   600  	if result.Err != nil {
   601  		return nil, result.Err
   602  	}
   603  	user := result.Data.(*model.User)
   604  
   605  	if err := a.RevokeAllSessions(user.Id); err != nil {
   606  		return nil, err
   607  	}
   608  
   609  	if result = <-a.Srv.Store.User().UpdateAuthData(user.Id, service, &authData, ssoEmail, true); result.Err != nil {
   610  		return nil, result.Err
   611  	}
   612  
   613  	a.Srv.Go(func() {
   614  		if err := a.SendSignInChangeEmail(user.Email, strings.Title(service)+" SSO", user.Locale, a.GetSiteURL()); err != nil {
   615  			mlog.Error(err.Error())
   616  		}
   617  	})
   618  
   619  	return user, nil
   620  }
   621  
   622  func (a *App) CreateOAuthStateToken(extra string) (*model.Token, *model.AppError) {
   623  	token := model.NewToken(model.TOKEN_TYPE_OAUTH, extra)
   624  
   625  	if result := <-a.Srv.Store.Token().Save(token); result.Err != nil {
   626  		return nil, result.Err
   627  	}
   628  
   629  	return token, nil
   630  }
   631  
   632  func (a *App) GetOAuthStateToken(token string) (*model.Token, *model.AppError) {
   633  	result := <-a.Srv.Store.Token().GetByToken(token)
   634  	if result.Err != nil {
   635  		return nil, model.NewAppError("GetOAuthStateToken", "api.oauth.invalid_state_token.app_error", nil, result.Err.Error(), http.StatusBadRequest)
   636  	}
   637  	mToken := result.Data.(*model.Token)
   638  
   639  	if mToken.Type != model.TOKEN_TYPE_OAUTH {
   640  		return nil, model.NewAppError("GetOAuthStateToken", "api.oauth.invalid_state_token.app_error", nil, "", http.StatusBadRequest)
   641  	}
   642  
   643  	return mToken, nil
   644  }
   645  
   646  func (a *App) GetAuthorizationCode(w http.ResponseWriter, r *http.Request, service string, props map[string]string, loginHint string) (string, *model.AppError) {
   647  	sso := a.Config().GetSSOService(service)
   648  	if sso == nil || !*sso.Enable {
   649  		return "", model.NewAppError("GetAuthorizationCode", "api.user.get_authorization_code.unsupported.app_error", nil, "service="+service, http.StatusNotImplemented)
   650  	}
   651  
   652  	secure := false
   653  	if GetProtocol(r) == "https" {
   654  		secure = true
   655  	}
   656  
   657  	cookieValue := model.NewId()
   658  	expiresAt := time.Unix(model.GetMillis()/1000+int64(OAUTH_COOKIE_MAX_AGE_SECONDS), 0)
   659  	oauthCookie := &http.Cookie{
   660  		Name:     COOKIE_OAUTH,
   661  		Value:    cookieValue,
   662  		Path:     "/",
   663  		MaxAge:   OAUTH_COOKIE_MAX_AGE_SECONDS,
   664  		Expires:  expiresAt,
   665  		HttpOnly: true,
   666  		Secure:   secure,
   667  	}
   668  
   669  	http.SetCookie(w, oauthCookie)
   670  
   671  	clientId := *sso.Id
   672  	endpoint := *sso.AuthEndpoint
   673  	scope := *sso.Scope
   674  
   675  	tokenExtra := generateOAuthStateTokenExtra(props["email"], props["action"], cookieValue)
   676  	stateToken, err := a.CreateOAuthStateToken(tokenExtra)
   677  	if err != nil {
   678  		return "", err
   679  	}
   680  
   681  	props["token"] = stateToken.Token
   682  	state := b64.StdEncoding.EncodeToString([]byte(model.MapToJson(props)))
   683  
   684  	siteUrl := a.GetSiteURL()
   685  	if strings.TrimSpace(siteUrl) == "" {
   686  		siteUrl = GetProtocol(r) + "://" + r.Host
   687  	}
   688  
   689  	redirectUri := siteUrl + "/signup/" + service + "/complete"
   690  
   691  	authUrl := endpoint + "?response_type=code&client_id=" + clientId + "&redirect_uri=" + url.QueryEscape(redirectUri) + "&state=" + url.QueryEscape(state)
   692  
   693  	if len(scope) > 0 {
   694  		authUrl += "&scope=" + utils.UrlEncode(scope)
   695  	}
   696  
   697  	if len(loginHint) > 0 {
   698  		authUrl += "&login_hint=" + utils.UrlEncode(loginHint)
   699  	}
   700  
   701  	return authUrl, nil
   702  }
   703  
   704  func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service, code, state, redirectUri string) (io.ReadCloser, string, map[string]string, *model.AppError) {
   705  	sso := a.Config().GetSSOService(service)
   706  	if sso == nil || !*sso.Enable {
   707  		return nil, "", nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.unsupported.app_error", nil, "service="+service, http.StatusNotImplemented)
   708  	}
   709  
   710  	b, strErr := b64.StdEncoding.DecodeString(state)
   711  	if strErr != nil {
   712  		return nil, "", nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, strErr.Error(), http.StatusBadRequest)
   713  	}
   714  
   715  	stateStr := string(b)
   716  
   717  	stateProps := model.MapFromJson(strings.NewReader(stateStr))
   718  
   719  	expectedToken, appErr := a.GetOAuthStateToken(stateProps["token"])
   720  	if appErr != nil {
   721  		return nil, "", stateProps, appErr
   722  	}
   723  
   724  	stateEmail := stateProps["email"]
   725  	stateAction := stateProps["action"]
   726  	if stateAction == model.OAUTH_ACTION_EMAIL_TO_SSO && stateEmail == "" {
   727  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest)
   728  	}
   729  
   730  	cookie, cookieErr := r.Cookie(COOKIE_OAUTH)
   731  	if cookieErr != nil {
   732  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest)
   733  	}
   734  
   735  	expectedTokenExtra := generateOAuthStateTokenExtra(stateEmail, stateAction, cookie.Value)
   736  	if expectedTokenExtra != expectedToken.Extra {
   737  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest)
   738  	}
   739  
   740  	appErr = a.DeleteToken(expectedToken)
   741  	if appErr != nil {
   742  		mlog.Error(appErr.Error())
   743  	}
   744  
   745  	httpCookie := &http.Cookie{
   746  		Name:     COOKIE_OAUTH,
   747  		Value:    "",
   748  		Path:     "/",
   749  		MaxAge:   -1,
   750  		HttpOnly: true,
   751  	}
   752  
   753  	http.SetCookie(w, httpCookie)
   754  
   755  	teamId := stateProps["team_id"]
   756  
   757  	p := url.Values{}
   758  	p.Set("client_id", *sso.Id)
   759  	p.Set("client_secret", *sso.Secret)
   760  	p.Set("code", code)
   761  	p.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
   762  	p.Set("redirect_uri", redirectUri)
   763  
   764  	req, requestErr := http.NewRequest("POST", *sso.TokenEndpoint, strings.NewReader(p.Encode()))
   765  	if requestErr != nil {
   766  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.token_failed.app_error", nil, requestErr.Error(), http.StatusInternalServerError)
   767  	}
   768  
   769  	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   770  	req.Header.Set("Accept", "application/json")
   771  
   772  	resp, err := a.HTTPService.MakeClient(true).Do(req)
   773  	if err != nil {
   774  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.token_failed.app_error", nil, err.Error(), http.StatusInternalServerError)
   775  	}
   776  	defer resp.Body.Close()
   777  
   778  	var buf bytes.Buffer
   779  	tee := io.TeeReader(resp.Body, &buf)
   780  	ar := model.AccessResponseFromJson(tee)
   781  
   782  	if ar == nil || resp.StatusCode != http.StatusOK {
   783  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_response.app_error", nil, "response_body="+buf.String(), http.StatusInternalServerError)
   784  	}
   785  
   786  	if strings.ToLower(ar.TokenType) != model.ACCESS_TOKEN_TYPE {
   787  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_token.app_error", nil, "token_type="+ar.TokenType+", response_body="+buf.String(), http.StatusInternalServerError)
   788  	}
   789  
   790  	if len(ar.AccessToken) == 0 {
   791  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.missing.app_error", nil, "response_body="+buf.String(), http.StatusInternalServerError)
   792  	}
   793  
   794  	p = url.Values{}
   795  	p.Set("access_token", ar.AccessToken)
   796  	req, requestErr = http.NewRequest("GET", *sso.UserApiEndpoint, strings.NewReader(""))
   797  	if requestErr != nil {
   798  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.service.app_error", map[string]interface{}{"Service": service}, requestErr.Error(), http.StatusInternalServerError)
   799  	}
   800  
   801  	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   802  	req.Header.Set("Accept", "application/json")
   803  	req.Header.Set("Authorization", "Bearer "+ar.AccessToken)
   804  
   805  	resp, err = a.HTTPService.MakeClient(true).Do(req)
   806  	if err != nil {
   807  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.service.app_error", map[string]interface{}{"Service": service}, err.Error(), http.StatusInternalServerError)
   808  	} else if resp.StatusCode != http.StatusOK {
   809  		defer resp.Body.Close()
   810  
   811  		// Ignore the error below because the resulting string will just be the empty string if bodyBytes is nil
   812  		bodyBytes, _ := ioutil.ReadAll(resp.Body)
   813  		bodyString := string(bodyBytes)
   814  
   815  		mlog.Error("Error getting OAuth user: " + bodyString)
   816  
   817  		if service == model.SERVICE_GITLAB && resp.StatusCode == http.StatusForbidden && strings.Contains(bodyString, "Terms of Service") {
   818  			// Return a nicer error when the user hasn't accepted GitLab's terms of service
   819  			return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "oauth.gitlab.tos.error", nil, "", http.StatusBadRequest)
   820  		}
   821  
   822  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.response.app_error", nil, "response_body="+bodyString, http.StatusInternalServerError)
   823  	}
   824  
   825  	// Note that resp.Body is not closed here, so it must be closed by the caller
   826  	return resp.Body, teamId, stateProps, nil
   827  }
   828  
   829  func (a *App) SwitchEmailToOAuth(w http.ResponseWriter, r *http.Request, email, password, code, service string) (string, *model.AppError) {
   830  	if a.License() != nil && !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer {
   831  		return "", model.NewAppError("emailToOAuth", "api.user.email_to_oauth.not_available.app_error", nil, "", http.StatusForbidden)
   832  	}
   833  
   834  	user, err := a.GetUserByEmail(email)
   835  	if err != nil {
   836  		return "", err
   837  	}
   838  
   839  	if err = a.CheckPasswordAndAllCriteria(user, password, code); err != nil {
   840  		return "", err
   841  	}
   842  
   843  	stateProps := map[string]string{}
   844  	stateProps["action"] = model.OAUTH_ACTION_EMAIL_TO_SSO
   845  	stateProps["email"] = email
   846  
   847  	if service == model.USER_AUTH_SERVICE_SAML {
   848  		return a.GetSiteURL() + "/login/sso/saml?action=" + model.OAUTH_ACTION_EMAIL_TO_SSO + "&email=" + utils.UrlEncode(email), nil
   849  	}
   850  
   851  	authUrl, err := a.GetAuthorizationCode(w, r, service, stateProps, "")
   852  	if err != nil {
   853  		return "", err
   854  	}
   855  
   856  	return authUrl, nil
   857  }
   858  
   859  func (a *App) SwitchOAuthToEmail(email, password, requesterId string) (string, *model.AppError) {
   860  	if a.License() != nil && !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer {
   861  		return "", model.NewAppError("oauthToEmail", "api.user.oauth_to_email.not_available.app_error", nil, "", http.StatusForbidden)
   862  	}
   863  
   864  	user, err := a.GetUserByEmail(email)
   865  	if err != nil {
   866  		return "", err
   867  	}
   868  
   869  	if user.Id != requesterId {
   870  		return "", model.NewAppError("SwitchOAuthToEmail", "api.user.oauth_to_email.context.app_error", nil, "", http.StatusForbidden)
   871  	}
   872  
   873  	if err := a.UpdatePassword(user, password); err != nil {
   874  		return "", err
   875  	}
   876  
   877  	T := utils.GetUserTranslations(user.Locale)
   878  
   879  	a.Srv.Go(func() {
   880  		if err := a.SendSignInChangeEmail(user.Email, T("api.templates.signin_change_email.body.method_email"), user.Locale, a.GetSiteURL()); err != nil {
   881  			mlog.Error(err.Error())
   882  		}
   883  	})
   884  
   885  	if err := a.RevokeAllSessions(requesterId); err != nil {
   886  		return "", err
   887  	}
   888  
   889  	return "/login?extra=signin_change", nil
   890  }
   891  
   892  func generateOAuthStateTokenExtra(email, action, cookie string) string {
   893  	return email + ":" + action + ":" + cookie
   894  }