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