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