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