github.com/minio/console@v1.3.0/api/user_logout.go (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2021 MinIO, Inc. 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Affero General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 package api 18 19 import ( 20 "context" 21 "crypto/tls" 22 "encoding/base64" 23 "encoding/json" 24 "net/http" 25 "net/url" 26 "time" 27 28 "github.com/go-openapi/runtime" 29 "github.com/go-openapi/runtime/middleware" 30 "github.com/minio/console/api/operations" 31 authApi "github.com/minio/console/api/operations/auth" 32 "github.com/minio/console/models" 33 "github.com/minio/console/pkg/auth/idp/oauth2" 34 ) 35 36 func registerLogoutHandlers(api *operations.ConsoleAPI) { 37 // logout from console 38 api.AuthLogoutHandler = authApi.LogoutHandlerFunc(func(params authApi.LogoutParams, session *models.Principal) middleware.Responder { 39 err := getLogoutResponse(session, params) 40 if err != nil { 41 api.Logger("IDP logout failed: %v", err.APIError) 42 } 43 // Custom response writer to expire the session cookies 44 return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) { 45 expiredCookie := ExpireSessionCookie() 46 // this will tell the browser to clear the cookie and invalidate user session 47 // additionally we are deleting the cookie from the client side 48 http.SetCookie(w, &expiredCookie) 49 http.SetCookie(w, &http.Cookie{ 50 Path: "/", 51 Name: "idp-refresh-token", 52 Value: "", 53 MaxAge: -1, 54 Expires: time.Now().Add(-100 * time.Hour), 55 HttpOnly: true, 56 Secure: len(GlobalPublicCerts) > 0, 57 SameSite: http.SameSiteLaxMode, 58 }) 59 authApi.NewLogoutOK().WriteResponse(w, p) 60 }) 61 }) 62 } 63 64 // logout() call Expire() on the provided ConsoleCredentials 65 func logout(credentials ConsoleCredentialsI) { 66 credentials.Expire() 67 } 68 69 // getLogoutResponse performs logout() and returns nil or errors 70 func getLogoutResponse(session *models.Principal, params authApi.LogoutParams) *CodedAPIError { 71 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 72 defer cancel() 73 state := params.Body.State 74 if state != "" { 75 if err := logoutFromIDPProvider(params.HTTPRequest, state); err != nil { 76 return ErrorWithContext(ctx, err) 77 } 78 } 79 creds := getConsoleCredentialsFromSession(session) 80 credentials := ConsoleCredentials{ConsoleCredentials: creds} 81 logout(credentials) 82 return nil 83 } 84 85 func logoutFromIDPProvider(r *http.Request, state string) error { 86 decodedRState, err := base64.StdEncoding.DecodeString(state) 87 if err != nil { 88 return err 89 } 90 var requestItems oauth2.LoginURLParams 91 err = json.Unmarshal(decodedRState, &requestItems) 92 if err != nil { 93 return err 94 } 95 providerCfg := GlobalMinIOConfig.OpenIDProviders[requestItems.IDPName] 96 refreshToken, err := r.Cookie("idp-refresh-token") 97 if err != nil { 98 return err 99 } 100 if providerCfg.EndSessionEndpoint != "" { 101 params := url.Values{} 102 params.Add("client_id", providerCfg.ClientID) 103 params.Add("client_secret", providerCfg.ClientSecret) 104 params.Add("refresh_token", refreshToken.Value) 105 client := &http.Client{ 106 Transport: &http.Transport{ 107 TLSClientConfig: &tls.Config{ 108 RootCAs: GlobalRootCAs, 109 }, 110 }, 111 } 112 _, err := client.PostForm(providerCfg.EndSessionEndpoint, params) 113 if err != nil { 114 return err 115 } 116 } 117 return nil 118 }