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