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