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 }