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