diff --git a/operatorapi/operator_login.go b/operatorapi/operator_login.go index 97968585d..b01d366fc 100644 --- a/operatorapi/operator_login.go +++ b/operatorapi/operator_login.go @@ -55,8 +55,10 @@ func registerLoginHandlers(api *operations.OperatorAPI) { } // Custom response writer to set the session cookies return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) { - cookie := restapi.NewSessionCookieForConsole(loginResponse.SessionID) - http.SetCookie(w, &cookie) + cookies := restapi.NewSessionCookieForConsole(loginResponse.SessionID) + for _, cookie := range cookies { + http.SetCookie(w, &cookie) + } user_api.NewLoginCreated().WithPayload(loginResponse).WriteResponse(w, p) }) }) @@ -67,8 +69,10 @@ func registerLoginHandlers(api *operations.OperatorAPI) { } // Custom response writer to set the session cookies return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) { - cookie := restapi.NewSessionCookieForConsole(loginResponse.SessionID) - http.SetCookie(w, &cookie) + cookies := restapi.NewSessionCookieForConsole(loginResponse.SessionID) + for _, cookie := range cookies { + http.SetCookie(w, &cookie) + } user_api.NewLoginOauth2AuthCreated().WithPayload(loginResponse).WriteResponse(w, p) }) }) @@ -79,8 +83,10 @@ func registerLoginHandlers(api *operations.OperatorAPI) { } // Custom response writer to set the session cookies return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) { - cookie := restapi.NewSessionCookieForConsole(loginResponse.SessionID) - http.SetCookie(w, &cookie) + cookies := restapi.NewSessionCookieForConsole(loginResponse.SessionID) + for _, cookie := range cookies { + http.SetCookie(w, &cookie) + } user_api.NewLoginOperatorCreated().WithPayload(loginResponse).WriteResponse(w, p) }) }) diff --git a/pkg/auth/token.go b/pkg/auth/token.go index 3754da522..69d2e4fbb 100644 --- a/pkg/auth/token.go +++ b/pkg/auth/token.go @@ -287,6 +287,7 @@ func decrypt(ciphertext []byte, associatedData []byte) ([]byte, error) { func GetTokenFromRequest(r *http.Request) (string, error) { // Token might come either as a Cookie or as a Header // if not set in cookie, check if it is set on Header. + tokenCookie, err := r.Cookie("token") if err != nil { return "", ErrNoAuthToken @@ -295,7 +296,16 @@ func GetTokenFromRequest(r *http.Request) (string, error) { if tokenCookie.Expires.After(currentTime) { return "", errTokenExpired } - return strings.TrimSpace(tokenCookie.Value), nil + + mergeToken := strings.TrimSpace(tokenCookie.Value) + for _, cookie := range r.Cookies() { + if cookie.Name != "token" && strings.HasPrefix(cookie.Name, "token") { + mergeToken = fmt.Sprintf("%s%s", mergeToken, strings.TrimSpace(cookie.Value)) + } + } + + return mergeToken, nil + } func GetClaimsFromTokenInRequest(req *http.Request) (*models.Principal, error) { diff --git a/portal-ui/src/common/utils.ts b/portal-ui/src/common/utils.ts index b72dab668..f6e7205a4 100644 --- a/portal-ui/src/common/utils.ts +++ b/portal-ui/src/common/utils.ts @@ -76,6 +76,9 @@ export const deleteCookie = (name: string) => { export const clearSession = () => { storage.removeItem("token"); deleteCookie("token"); + for (let i = 1; i < 10; i++) { + deleteCookie(`token${i}`); + } }; // timeFromDate gets time string from date input diff --git a/portal-ui/src/screens/Console/Common/ModalWrapper/ModalWrapper.tsx b/portal-ui/src/screens/Console/Common/ModalWrapper/ModalWrapper.tsx index 11263d4e7..bd20ca26c 100644 --- a/portal-ui/src/screens/Console/Common/ModalWrapper/ModalWrapper.tsx +++ b/portal-ui/src/screens/Console/Common/ModalWrapper/ModalWrapper.tsx @@ -136,7 +136,7 @@ const ModalWrapper = ({ return; } // Open SnackBar - if(modalSnackMessage.type !== "error") { + if (modalSnackMessage.type !== "error") { setOpenSnackbar(true); } } diff --git a/restapi/user_account.go b/restapi/user_account.go index 2dd5d741f..0e02cf664 100644 --- a/restapi/user_account.go +++ b/restapi/user_account.go @@ -39,8 +39,10 @@ func registerAccountHandlers(api *operations.ConsoleAPI) { } // Custom response writer to update the session cookies return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) { - cookie := NewSessionCookieForConsole(changePasswordResponse.SessionID) - http.SetCookie(w, &cookie) + cookies := NewSessionCookieForConsole(changePasswordResponse.SessionID) + for _, cookie := range cookies { + http.SetCookie(w, &cookie) + } user_api.NewLoginCreated().WithPayload(changePasswordResponse).WriteResponse(w, p) }) }) diff --git a/restapi/user_login.go b/restapi/user_login.go index 5eb08401d..4609b4578 100644 --- a/restapi/user_login.go +++ b/restapi/user_login.go @@ -53,8 +53,10 @@ func registerLoginHandlers(api *operations.ConsoleAPI) { } // Custom response writer to set the session cookies return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) { - cookie := NewSessionCookieForConsole(loginResponse.SessionID) - http.SetCookie(w, &cookie) + cookies := NewSessionCookieForConsole(loginResponse.SessionID) + for _, cookie := range cookies { + http.SetCookie(w, &cookie) + } user_api.NewLoginCreated().WithPayload(loginResponse).WriteResponse(w, p) }) }) @@ -65,8 +67,10 @@ func registerLoginHandlers(api *operations.ConsoleAPI) { } // Custom response writer to set the session cookies return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) { - cookie := NewSessionCookieForConsole(loginResponse.SessionID) - http.SetCookie(w, &cookie) + cookies := NewSessionCookieForConsole(loginResponse.SessionID) + for _, cookie := range cookies { + http.SetCookie(w, &cookie) + } user_api.NewLoginOauth2AuthCreated().WithPayload(loginResponse).WriteResponse(w, p) }) }) @@ -77,8 +81,10 @@ func registerLoginHandlers(api *operations.ConsoleAPI) { } // Custom response writer to set the session cookies return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) { - cookie := NewSessionCookieForConsole(loginResponse.SessionID) - http.SetCookie(w, &cookie) + cookies := NewSessionCookieForConsole(loginResponse.SessionID) + for _, cookie := range cookies { + http.SetCookie(w, &cookie) + } user_api.NewLoginOperatorCreated().WithPayload(loginResponse).WriteResponse(w, p) }) }) diff --git a/restapi/utils.go b/restapi/utils.go index ca34f12f4..a3f6ba2a8 100644 --- a/restapi/utils.go +++ b/restapi/utils.go @@ -18,6 +18,7 @@ package restapi import ( "crypto/rand" + "fmt" "io" "net/http" "os" @@ -105,22 +106,73 @@ func FileExists(filename string) bool { return !info.IsDir() } -func NewSessionCookieForConsole(token string) http.Cookie { - expiration := time.Now().Add(SessionDuration) +func NewSessionCookieForConsole(token string) []http.Cookie { + const CookieChunk = 3800 - return http.Cookie{ - Path: "/", - Name: "token", - Value: token, - MaxAge: int(SessionDuration.Seconds()), // 45 minutes - Expires: expiration, - HttpOnly: true, - // if len(GlobalPublicCerts) > 0 is true, that means Console is running with TLS enable and the browser - // should not leak any cookie if we access the site using HTTP - Secure: len(GlobalPublicCerts) > 0, - // read more: https://web.dev/samesite-cookies-explained/ - SameSite: http.SameSiteLaxMode, + expiration := time.Now().Add(SessionDuration) + var cookies []http.Cookie + + i := 0 + cookieIndex := 0 + + for i < len(token) { + var until int + if i+CookieChunk < len(token) { + until = i + CookieChunk + } else { + until = len(token) + } + + cookieName := "token" + if len(cookies) > 0 { + cookieName = fmt.Sprintf("token%d", len(cookies)) + } + + cookie := http.Cookie{ + Path: "/", + Name: cookieName, + Value: token[i:until], + MaxAge: int(SessionDuration.Seconds()), // 45 minutes + Expires: expiration, + HttpOnly: true, + // if len(GlobalPublicCerts) > 0 is true, that means Console is running with TLS enable and the browser + // should not leak any cookie if we access the site using HTTP + Secure: len(GlobalPublicCerts) > 0, + // read more: https://web.dev/samesite-cookies-explained/ + SameSite: http.SameSiteLaxMode, + } + + cookies = append(cookies, cookie) + i += until + cookieIndex++ } + + // clear old cookies + expiredDuration := time.Now().Add(-1 * time.Second) + for i := cookieIndex; i < 10; i++ { + cookieName := "token" + if len(cookies) > 0 { + cookieName = fmt.Sprintf("token%d", i) + } + + cookie := http.Cookie{ + Path: "/", + Name: cookieName, + Value: "", + MaxAge: 0, // 45 minutes + Expires: expiredDuration, + HttpOnly: true, + // if len(GlobalPublicCerts) > 0 is true, that means Console is running with TLS enable and the browser + // should not leak any cookie if we access the site using HTTP + Secure: len(GlobalPublicCerts) > 0, + // read more: https://web.dev/samesite-cookies-explained/ + SameSite: http.SameSiteLaxMode, + } + + cookies = append(cookies, cookie) + } + + return cookies } func ExpireSessionCookie() http.Cookie {