Call end_session_endpoint in IDP provider when login out from Console (#2476)

This commit is contained in:
Javier Adriel
2022-12-05 18:14:41 -06:00
committed by GitHub
parent 262a601d21
commit e7a41b4cd9
13 changed files with 243 additions and 7 deletions

View File

@@ -3460,6 +3460,16 @@ func init() {
],
"summary": "Logout from Console.",
"operationId": "Logout",
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/logoutRequest"
}
}
],
"responses": {
"200": {
"description": "A successful response."
@@ -6426,11 +6436,22 @@ func init() {
"loginResponse": {
"type": "object",
"properties": {
"IDPRefreshToken": {
"type": "string"
},
"sessionId": {
"type": "string"
}
}
},
"logoutRequest": {
"type": "object",
"properties": {
"state": {
"type": "string"
}
}
},
"makeBucketRequest": {
"type": "object",
"required": [
@@ -11615,6 +11636,16 @@ func init() {
],
"summary": "Logout from Console.",
"operationId": "Logout",
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/logoutRequest"
}
}
],
"responses": {
"200": {
"description": "A successful response."
@@ -14707,11 +14738,22 @@ func init() {
"loginResponse": {
"type": "object",
"properties": {
"IDPRefreshToken": {
"type": "string"
},
"sessionId": {
"type": "string"
}
}
},
"logoutRequest": {
"type": "object",
"properties": {
"state": {
"type": "string"
}
}
},
"makeBucketRequest": {
"type": "object",
"required": [

View File

@@ -23,10 +23,15 @@ package auth
// Editing this file might prove futile when you re-run the swagger generate command
import (
"io"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/validate"
"github.com/minio/console/models"
)
// NewLogoutParams creates a new LogoutParams object
@@ -45,6 +50,12 @@ type LogoutParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*
Required: true
In: body
*/
Body *models.LogoutRequest
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
@@ -56,6 +67,33 @@ func (o *LogoutParams) BindRequest(r *http.Request, route *middleware.MatchedRou
o.HTTPRequest = r
if runtime.HasBody(r) {
defer r.Body.Close()
var body models.LogoutRequest
if err := route.Consumer.Consume(r.Body, &body); err != nil {
if err == io.EOF {
res = append(res, errors.Required("body", "body", ""))
} else {
res = append(res, errors.NewParseError("body", "body", "", err))
}
} else {
// validate body object
if err := body.Validate(route.Formats); err != nil {
res = append(res, err)
}
ctx := validate.WithOperationRequest(r.Context())
if err := body.ContextValidate(ctx, route.Formats); err != nil {
res = append(res, err)
}
if len(res) == 0 {
o.Body = &body
}
}
} else {
res = append(res, errors.Required("body", "body", ""))
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}

View File

@@ -65,6 +65,14 @@ func registerLoginHandlers(api *operations.ConsoleAPI) {
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
cookie := NewSessionCookieForConsole(loginResponse.SessionID)
http.SetCookie(w, &cookie)
http.SetCookie(w, &http.Cookie{
Path: "/",
Name: "idp-refresh-token",
Value: loginResponse.IDPRefreshToken,
HttpOnly: true,
Secure: len(GlobalPublicCerts) > 0,
SameSite: http.SameSiteLaxMode,
})
authApi.NewLoginOauth2AuthNoContent().WriteResponse(w, p)
})
})
@@ -252,7 +260,8 @@ func getLoginOauth2AuthResponse(params authApi.LoginOauth2AuthParams, openIDProv
}
// serialize output
loginResponse := &models.LoginResponse{
SessionID: *token,
SessionID: *token,
IDPRefreshToken: identityProvider.Client.RefreshToken,
}
return loginResponse, nil
}

View File

@@ -17,11 +17,17 @@
package restapi
import (
"context"
"encoding/base64"
"encoding/json"
"net/http"
"net/url"
"time"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/console/models"
"github.com/minio/console/pkg/auth/idp/oauth2"
"github.com/minio/console/restapi/operations"
authApi "github.com/minio/console/restapi/operations/auth"
)
@@ -29,13 +35,26 @@ import (
func registerLogoutHandlers(api *operations.ConsoleAPI) {
// logout from console
api.AuthLogoutHandler = authApi.LogoutHandlerFunc(func(params authApi.LogoutParams, session *models.Principal) middleware.Responder {
getLogoutResponse(session)
err := getLogoutResponse(session, params)
if err != nil {
return authApi.NewLogoutDefault(int(err.Code)).WithPayload(err)
}
// Custom response writer to expire the session cookies
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
expiredCookie := ExpireSessionCookie()
// this will tell the browser to clear the cookie and invalidate user session
// additionally we are deleting the cookie from the client side
http.SetCookie(w, &expiredCookie)
http.SetCookie(w, &http.Cookie{
Path: "/",
Name: "idp-refresh-token",
Value: "",
MaxAge: -1,
Expires: time.Now().Add(-100 * time.Hour),
HttpOnly: true,
Secure: len(GlobalPublicCerts) > 0,
SameSite: http.SameSiteLaxMode,
})
authApi.NewLogoutOK().WriteResponse(w, p)
})
})
@@ -47,8 +66,45 @@ func logout(credentials ConsoleCredentialsI) {
}
// getLogoutResponse performs logout() and returns nil or errors
func getLogoutResponse(session *models.Principal) {
func getLogoutResponse(session *models.Principal, params authApi.LogoutParams) *models.Error {
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
defer cancel()
state := params.Body.State
if state != "" {
if err := logoutFromIDPProvider(params.HTTPRequest, state); err != nil {
return ErrorWithContext(ctx, err)
}
}
creds := getConsoleCredentialsFromSession(session)
credentials := ConsoleCredentials{ConsoleCredentials: creds}
logout(credentials)
return nil
}
func logoutFromIDPProvider(r *http.Request, state string) error {
decodedRState, err := base64.StdEncoding.DecodeString(state)
if err != nil {
return err
}
var requestItems oauth2.LoginURLParams
err = json.Unmarshal(decodedRState, &requestItems)
if err != nil {
return err
}
providerCfg := GlobalMinIOConfig.OpenIDProviders[requestItems.IDPName]
refreshToken, err := r.Cookie("idp-refresh-token")
if err != nil {
return err
}
if providerCfg.EndSessionEndpoint != "" {
params := url.Values{}
params.Add("client_id", providerCfg.ClientID)
params.Add("client_secret", providerCfg.ClientSecret)
params.Add("refresh_token", refreshToken.Value)
_, err := http.PostForm(providerCfg.EndSessionEndpoint, params)
if err != nil {
return err
}
}
return nil
}