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