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  }