github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/app/oauth.go (about)

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