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