You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by lm...@apache.org on 2021/08/27 23:10:40 UTC

[knox] branch master updated: KNOX-2625 - initial commit (#476)

This is an automated email from the ASF dual-hosted git repository.

lmccay pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git


The following commit(s) were added to refs/heads/master by this push:
     new 762ba37  KNOX-2625 - initial commit (#476)
762ba37 is described below

commit 762ba37f69f314f5463eeb5e7e8b8a4a28c8491c
Author: lmccay <lm...@apache.org>
AuthorDate: Fri Aug 27 19:10:36 2021 -0400

    KNOX-2625 - initial commit (#476)
    
    * KNOX-2625 - Enhance KnoxSSO to Support Session Timeout and Logout
---
 gateway-applications/pom.xml                       |   4 +
 .../applications/knoxauth/app/js/knoxauth.js       | 112 +++++++-------
 .../resources/applications/knoxauth/app/logout.jsp | 164 +++++++++++++++++++++
 .../applications/knoxauth/app/redirecting.jsp      |  11 +-
 .../applications/knoxauth/app/styles/knox.css      |   9 ++
 .../provider/federation/SSOCookieProviderTest.java |  34 +++++
 .../gateway/config/impl/GatewayConfigImpl.java     |   7 +
 .../service/session/SessionInformation.java        |  23 +++
 .../gateway/service/session/SessionResource.java   |  16 +-
 .../apache/knox/gateway/config/GatewayConfig.java  |   5 +
 .../org/apache/knox/gateway/GatewayTestConfig.java |   5 +
 knox-homepage-ui/home/app/homepage.service.ts      |  17 ++-
 .../session.information.component.ts               |  16 +-
 .../app/sessionInformation/session.information.ts  |   2 +
 knox-homepage-ui/home/index.html                   |   4 +
 15 files changed, 364 insertions(+), 65 deletions(-)

diff --git a/gateway-applications/pom.xml b/gateway-applications/pom.xml
index 1c741f0..4180471 100644
--- a/gateway-applications/pom.xml
+++ b/gateway-applications/pom.xml
@@ -44,6 +44,10 @@
         </dependency>
         <dependency>
             <groupId>org.apache.knox</groupId>
+            <artifactId>gateway-spi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.knox</groupId>
             <artifactId>knox-token-management-ui</artifactId>
         </dependency>
     </dependencies>
diff --git a/gateway-applications/src/main/resources/applications/knoxauth/app/js/knoxauth.js b/gateway-applications/src/main/resources/applications/knoxauth/app/js/knoxauth.js
index aff8bfc..f503cde 100644
--- a/gateway-applications/src/main/resources/applications/knoxauth/app/js/knoxauth.js
+++ b/gateway-applications/src/main/resources/applications/knoxauth/app/js/knoxauth.js
@@ -19,70 +19,74 @@ var loginPageSuffix = "/knoxauth/login.html";
 var webssoURL = "/api/v1/websso?originalUrl=";
 var userAgent = navigator.userAgent.toLowerCase();
 
-function get(name){
-    //KNOX-820 changing the regex so that multiple query params get included with the 'originalUrl'
-   if(name=(new RegExp('[?&]'+encodeURIComponent(name)+'=([^]*)')).exec(location.search))
-      return decodeURIComponent(name[1]);
+function get(name) {
+	//KNOX-820 changing the regex so that multiple query params get included with the 'originalUrl'
+	if ((name = (new RegExp('[?&]' + encodeURIComponent(name) + '=([^]*)')).exec(location.search))) {
+		return decodeURIComponent(name[1]);
+	}
 }
 
 function testSameOrigin(url) {
-    var loc = window.location,
-        a = document.createElement('a');
-    a.href = url;
-    return a.hostname == loc.hostname &&
-           a.port == loc.port &&
-           a.protocol == loc.protocol;
+	var loc = window.location,
+		a = document.createElement('a');
+	a.href = url;
+	return a.hostname == loc.hostname &&
+		a.port == loc.port &&
+		a.protocol == loc.protocol;
 }
 
 function redirect(redirectUrl) {
-  try { window.location.replace(redirectUrl); } 
-  catch(e) { window.location = redirectUrl; }
+	try {
+		window.location.replace(redirectUrl);
+	} catch (e) {
+		window.location = redirectUrl;
+	}
 }
 
 var keypressed = function(event) {
-    if (event.keyCode == 13) {
-        login();
-    }
-}
+	if (event.keyCode == 13) {
+		login();
+	}
+};
 
 var login = function() {
-    var pathname = window.location.pathname;
-    var topologyContext = pathname.replace(loginPageSuffix, "");;
-    var loginURL = topologyContext + webssoURL;
-    var form = document.forms[0];
-    var username = form.username.value;
-    var password = form.password.value;
-    var _login = function() {
-    var originalUrl = get("originalUrl");
-    var idpUrl = loginURL + originalUrl;
-    var redirectUrl = originalUrl;
-      //Instantiate HTTP Request
-        var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
-        request.open("POST", idpUrl, true);
-        request.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password))
-        request.send(null);
+	var pathname = window.location.pathname;
+	var topologyContext = pathname.replace(loginPageSuffix, "");
+	var loginURL = topologyContext + webssoURL;
+	var form = document.forms[0];
+	var username = form.username.value;
+	var password = form.password.value;
+	var _login = function() {
+		var originalUrl = get("originalUrl");
+		var idpUrl = loginURL + originalUrl;
+		var redirectUrl = originalUrl;
+		//Instantiate HTTP Request
+		var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
+		request.open("POST", idpUrl, true);
+		request.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password));
+		request.send(null);
 
-      //Process Response
-        request.onreadystatechange = function(){
-            if (request.readyState == 4) {
-                if (request.status==0 || request.status==200 || request.status==204 || request.status==307 || request.status==303) {
-                  if (testSameOrigin(originalUrl) == false) {
-                    redirectUrl = "redirecting.html?originalUrl=" + originalUrl;
-                  }
-                  redirect(redirectUrl);
-                } else {
-                  $('#errorBox').show();
-                  $('#signInLoading').hide();
-                  $('#signIn').removeAttr('disabled');
-                  if (request.status==401) {
-                    $('#errorBox .errorMsg').text("The username or password you entered is incorrect.");
-                  } else {
-                    $('#errorBox .errorMsg').text("Response from " + request.responseURL + " - " + request.status + ": " + request.statusText);
-                  }
-                }
-            }
-        }
-    }
+		//Process Response
+		request.onreadystatechange = function() {
+			if (request.readyState == 4) {
+				if (request.status == 0 || request.status == 200 || request.status == 204 || request.status == 307 || request.status == 303) {
+					if (testSameOrigin(originalUrl) == false) {
+						redirectUrl = "redirecting.html?originalUrl=" + originalUrl;
+					}
+					redirect(redirectUrl);
+				} else {
+					$('#errorBox').show();
+					$('#signInLoading').hide();
+					$('#signIn').removeAttr('disabled');
+					if (request.status == 401) {
+						$('#errorBox .errorMsg').text("The username or password you entered is incorrect.");
+					} else {
+						$('#errorBox .errorMsg').text("Response from " + request.responseURL + " - " + request.status + ": " + request.statusText);
+					}
+				}
+			}
+		};
+	};
 
-    _login();
-}
+	_login();
+};
diff --git a/gateway-applications/src/main/resources/applications/knoxauth/app/logout.jsp b/gateway-applications/src/main/resources/applications/knoxauth/app/logout.jsp
new file mode 100644
index 0000000..ebf020f
--- /dev/null
+++ b/gateway-applications/src/main/resources/applications/knoxauth/app/logout.jsp
@@ -0,0 +1,164 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+      http://www.apache.org/licenses/LICENSE-2.0
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<%@ page import="java.util.Collection" %>
+<%@ page import="java.util.Map" %>
+<%@ page import="org.apache.knox.gateway.topology.Topology" %>
+<%@ page import="org.apache.knox.gateway.topology.Service" %>
+<%@ page import="org.apache.knox.gateway.util.RegExUtils" %>
+<%@ page import="org.apache.knox.gateway.util.WhitelistUtils" %>
+<%@ page import="org.apache.knox.gateway.config.GatewayConfig" %>
+<%@ page import="java.net.MalformedURLException" %>
+<%@ page import="org.apache.knox.gateway.util.Urls" %>
+
+<!DOCTYPE html>
+<!--[if lt IE 7]><html class="no-js lt-ie9 lt-ie8 lt-ie7"><![endif]-->
+<!--[if IE 7]><html class="no-js lt-ie9 lt-ie8"><![endif]-->
+<!--[if IE 8]><html class="no-js lt-ie9"><![endif]-->
+<!--[if gt IE 8]><!-->
+<html class="no-js">
+    <!--<![endif]-->
+    <head>
+        <meta charset="utf-8">
+        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+        <meta name="description" content="">
+        <meta name="viewport" content="width=device-width">
+        <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
+        <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
+        <meta http-equiv="Pragma" content="no-cache">
+        <meta http-equiv="Expires" content="0">
+
+        <link rel="shortcut icon" href="images/favicon.ico">
+        <link href="styles/bootstrap.min.css" media="all" rel="stylesheet" type="text/css" id="bootstrap-css">
+        <link href="styles/knox.css" media="all" rel="stylesheet" type="text/css" >
+
+        <script src="libs/bower/jquery/js/jquery-3.5.1.min.js" ></script>
+
+        <script type="text/javascript" src="js/knoxauth.js"></script>
+    <%
+        String originalUrl = request.getParameter("originalUrl");
+        Topology topology = (Topology)request.getSession().getServletContext().getAttribute("org.apache.knox.gateway.topology");
+        String whitelist = null;
+        String cookieName = null;
+        GatewayConfig gatewayConfig =
+                (GatewayConfig) request.getServletContext().
+                getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
+        String globalLogoutPageURL = gatewayConfig.getGlobalLogoutPageUrl();
+        Collection<Service> services = topology.getServices();
+        for (Object service : services) {
+          Service svc = (Service)service;
+          if (svc.getRole().equals("KNOXSSO")) {
+            Map<String, String> params = svc.getParams();
+            whitelist = params.get("knoxsso.redirect.whitelist.regex");
+            // LJM TODO: get cookie name and possibly domain prefix info for use in logout
+            cookieName = params.get("knoxsso.cookie.name");
+            if (cookieName == null) {
+                cookieName = "hadoop-jwt";
+            }
+          }
+          break;
+        }
+        if (whitelist == null) {
+            whitelist = WhitelistUtils.getDispatchWhitelist(request);
+            if (whitelist == null) {
+                whitelist = "";
+            }
+        }
+
+        boolean validRedirect = false;
+        String origUrl = request.getParameter("originalUrl");
+        String del = "?";
+        if (origUrl != null && origUrl.contains("?")) {
+          del = "&";
+        }
+        if (origUrl != null) {
+          validRedirect = RegExUtils.checkWhitelist(whitelist, origUrl);
+        }
+        if (("1".equals(request.getParameter("returnToApp")))) {
+          if (validRedirect) {
+          	response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
+          	response.setHeader("Location",originalUrl + del + "refresh=1");
+            return;
+          }
+        }
+        else if (("1".equals(request.getParameter("globalLogout")))) {
+          Cookie c = new Cookie(cookieName, null);
+          c.setMaxAge(0);
+          c.setPath("/");
+          try {
+            String domainName = Urls.getDomainName(request.getRequestURL().toString(), null);
+            if(domainName != null) {
+              c.setDomain(domainName);
+            }
+          } catch (MalformedURLException e) {
+            // we are probably not going to be able to
+            // remove the cookie due to this error but it
+            // isn't necessarily not going to work.
+          }
+          response.addCookie(c);
+
+          response.setStatus(HttpServletResponse.SC_SEE_OTHER);
+          response.setHeader("Location", globalLogoutPageURL);
+          return;
+        }
+    %>
+  </head>
+  
+  <body class="login" style="">
+    <section id="signout-container" style="margin-top: 4.5px;">
+      <div class="l-logo">
+          <img src="images/knox-logo.gif" alt="Knox logo">
+      </div>
+        <%
+            if (validRedirect) {
+        %>
+          <h1 style="color: gray;">Session Termination</h1>
+          <div style="background: dark-gray;" class="l2-logo">
+              <p style="color: white;display: block">
+                Your session has timed out or you have attempted to logout of an application
+                that is participating in SSO. You may establish a new session by returning to
+                the application. If your previously established SSO session is still valid then
+                you will likely be automatically logged into your application. Otherwise, you
+                will be required to login again.
+                <a href="?returnToApp=1&originalUrl=<%= originalUrl %>" >Return to Application</a>
+              </p>
+        <%
+            if (globalLogoutPageURL != null && !globalLogoutPageURL.isEmpty()) {
+        %>
+              <p style="color: white;display: block">
+                If you would like to logout of the Knox SSO session, you need to do so from
+                the configured SSO provider. Subsequently, authentication will be required to access
+                any SSO protected resources. Note that this may or may not invalidate any previously
+                established application sessions. Application sessions are subject to their application
+                specific session cookies and timeouts.
+                <a href="<%= request.getRequestURI() %>?globalLogout=1" >Global Logout</a>
+              </p>
+          </div>
+        <%
+            }
+        } 
+        else {
+        %>
+        <div style="background: gray;text-color: white;text-align:center;">
+          <h1 style="color: red;">ERROR</h1>
+          <div style="background: white;" class="l-logo">
+          </div>
+          <p style="color: white;display: block">Invalid Redirect: Possible Phishing Attempt</p>
+        <%
+        }
+        %>
+        </div>
+    </section>
+  </body>
+</html>
diff --git a/gateway-applications/src/main/resources/applications/knoxauth/app/redirecting.jsp b/gateway-applications/src/main/resources/applications/knoxauth/app/redirecting.jsp
index 1be1c5b..290da28 100644
--- a/gateway-applications/src/main/resources/applications/knoxauth/app/redirecting.jsp
+++ b/gateway-applications/src/main/resources/applications/knoxauth/app/redirecting.jsp
@@ -41,6 +41,7 @@
 
         <script type="text/javascript" src="js/knoxauth.js"></script>
     <%
+        String originalUrl = request.getParameter("originalUrl");
         Topology topology = (Topology)request.getSession().getServletContext().getAttribute("org.apache.knox.gateway.topology");
         String whitelist = null;
         Collection services = topology.getServices();
@@ -57,14 +58,14 @@
                 whitelist = "";
             }
         }
-        boolean validRedirect = RegExUtils.checkWhitelist(whitelist, request.getParameter("originalUrl"));
+        boolean validRedirect = RegExUtils.checkWhitelist(whitelist, originalUrl);
         if (validRedirect) {
     %>
     <script>
     document.addEventListener("load", redirectOnLoad());
-    
+
     function redirectOnLoad() {
-      var originalUrl = get("originalUrl");
+      var originalUrl = <%= originalUrl %>;
       if (originalUrl != null) {
         redirect(originalUrl);
       }
@@ -74,7 +75,7 @@
     }
     %>
   </head>
-  
+ 
   <body>
         <section id="signin-container" style="margin-top: 80px;">
         <%
@@ -85,7 +86,7 @@
           <div style="background: white;" class="l-logo">
                 <img src="images/loading.gif" alt="Knox logo" style="text-align:center;width: 2%; height: 2%">
             </div>
-              <p style="color: white;display: block">Loading should complete in few a seconds. If not, click <a href="#" onclick='redirect(get("originalUrl"));' >here</a></p>
+              <p style="color: white;display: block">Loading should complete in few a seconds. If not, click <a href="<%= originalUrl %>">here</a></p>
         <%
         } else {
         %>
diff --git a/gateway-applications/src/main/resources/applications/knoxauth/app/styles/knox.css b/gateway-applications/src/main/resources/applications/knoxauth/app/styles/knox.css
index 2de4274..c130846 100644
--- a/gateway-applications/src/main/resources/applications/knoxauth/app/styles/knox.css
+++ b/gateway-applications/src/main/resources/applications/knoxauth/app/styles/knox.css
@@ -1588,11 +1588,20 @@ body.login {
   margin: 0 auto;
   width: 280px;
 }
+.login #signout-container {
+  margin: 0 auto;
+  width: 400px;
+}
 .login .l-logo {
   text-align: center;
   width: 100%;
   margin-bottom: 20px;
 }
+.login .l2-logo {
+  text-align: left;
+  width: 100%;
+  margin-bottom: 20px;
+}
 
 .icon-sign-in {
   margin-left: 15px;
diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java
index 5c932b1..679bf33 100644
--- a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java
+++ b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java
@@ -113,6 +113,40 @@ public class SSOCookieProviderTest extends AbstractJWTFilterTest {
   }
 
   @Test
+  public void testMissingCookie() throws Exception {
+    try {
+      Properties props = getProperties();
+      handler.init(new TestFilterConfig(props));
+
+      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+      EasyMock.expect(request.getCookies()).andReturn(null);
+      EasyMock.expect(request.getRequestURL()).andReturn(
+          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getQueryString()).andReturn(null);
+      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
+          SERVICE_URL);
+      response.sendRedirect(EasyMock.anyObject(String.class));
+      EasyMock.expectLastCall()
+      .andAnswer(() -> {
+          String newLocation = (String) EasyMock.getCurrentArguments()[0];
+          Assert.assertEquals("Not the expected principal", newLocation,
+              "https://localhost:8443/authserver?originalUrl=https://localhost:8888/resource");
+          return null;
+      });
+      EasyMock.replay(request);
+      EasyMock.replay(response);
+
+      TestFilterChain chain = new TestFilterChain();
+      handler.doFilter(request, response, chain);
+      Assert.assertFalse("doFilterCalled should be false.", chain.doFilterCalled );
+      Assert.assertNull("Subject should be null.", chain.subject);
+    } catch (ServletException se) {
+      fail("Should NOT have thrown a ServletException.");
+    }
+  }
+
+  @Test
   public void testNoProviderURLJWT() {
     try {
       Properties props = getProperties();
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java b/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
index 22ff816..1aa5fc3 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
@@ -274,6 +274,7 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig {
   private static final String KNOX_HOMEPAGE_HIDDEN_TOPOLOGIES =  "knox.homepage.hidden.topologies";
   private static final Set<String> KNOX_HOMEPAGE_HIDDEN_TOPOLOGIES_DEFAULT = new HashSet<>(Arrays.asList("admin", "manager", "knoxsso", "metadata", "homepage"));
   private static final String KNOX_HOMEPAGE_LOGOUT_ENABLED =  "knox.homepage.logout.enabled";
+  private static final String GLOBAL_LOGOUT_PAGE_URL = "knox.global.logout.page.url";
   private static final String KNOX_INCOMING_XFORWARDED_ENABLED = "gateway.incoming.xforwarded.enabled";
 
   //Gateway Database related properties
@@ -1224,6 +1225,12 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig {
   }
 
   @Override
+  public String getGlobalLogoutPageUrl() {
+    return get(GLOBAL_LOGOUT_PAGE_URL);
+  }
+
+
+  @Override
   public long getKeystoreCacheSizeLimit() {
     return getLong(KEYSTORE_CACHE_LIMIT, DEFAULT_KEYSTORE_CACHE_LIMIT);
   }
diff --git a/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionInformation.java b/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionInformation.java
index 74b57f5..e3822e0 100644
--- a/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionInformation.java
+++ b/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionInformation.java
@@ -28,6 +28,12 @@ public class SessionInformation {
   @XmlElement
   private String logoutUrl;
 
+  @XmlElement
+  private String logoutPageUrl;
+
+  @XmlElement
+  private String globalLogoutPageUrl;
+
   public String getUser() {
     return user;
   }
@@ -43,4 +49,21 @@ public class SessionInformation {
   public void setLogoutUrl(String logoutUrl) {
     this.logoutUrl = logoutUrl;
   }
+
+  public String getLogoutPageUrl() {
+    return logoutPageUrl;
+  }
+
+  public void setLogoutPageUrl(String logoutPageUrl) {
+    this.logoutPageUrl = logoutPageUrl;
+  }
+
+  public String getGlobalLogoutPageUrl() {
+    return globalLogoutPageUrl;
+  }
+
+  public void setGlobalLogoutPageUrl(String globalLogoutPageUrl) {
+    this.globalLogoutPageUrl = globalLogoutPageUrl;
+  }
+
 }
diff --git a/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionResource.java b/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionResource.java
index 2e5aa56..4bffced 100644
--- a/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionResource.java
+++ b/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionResource.java
@@ -54,12 +54,26 @@ public class SessionResource {
       String logoutUrl = getBaseGatewayUrl(config) + "/homepage/knoxssout/api/v1/webssout";
       LOG.homePageLogoutEnabled(logoutUrl);
       sessionInfo.setLogoutUrl(logoutUrl);
+      sessionInfo.setLogoutPageUrl(getLogoutPageUrl(config));
+      sessionInfo.setGlobalLogoutPageUrl(getGlobalLogoutPageUrl(config));
     }
 
     return sessionInfo;
   }
 
   private String getBaseGatewayUrl(GatewayConfig config) {
-    return request.getRequestURL().substring(0, request.getRequestURL().length() - request.getRequestURI().length()) + "/" + config.getGatewayPath();
+    return request.getRequestURL().substring(0,
+        request.getRequestURL().length() - request.getRequestURI().length()) +
+        "/" + config.getGatewayPath();
+  }
+
+  private String getLogoutPageUrl(GatewayConfig config) {
+    return getBaseGatewayUrl(config) +
+        "/knoxsso/knoxauth/logout.jsp?originalUrl=" + getBaseGatewayUrl(config) +
+        "/homepage/home";
+  }
+
+  private String getGlobalLogoutPageUrl(GatewayConfig config) {
+    return config.getGlobalLogoutPageUrl();
   }
 }
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java b/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
index 3599bc9..b6bd58b 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
@@ -739,6 +739,11 @@ public interface GatewayConfig {
   boolean homePageLogoutEnabled();
 
   /**
+   * @return the Global Logout Page for Federated IDPs
+   */
+  String getGlobalLogoutPageUrl();
+
+  /**
    * @return the maximum number of cache entries where keystore entries are stored
    */
   long getKeystoreCacheSizeLimit();
diff --git a/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java b/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
index 50b2046..19514a6 100644
--- a/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
+++ b/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
@@ -845,6 +845,11 @@ public class GatewayTestConfig extends Configuration implements GatewayConfig {
   }
 
   @Override
+  public String getGlobalLogoutPageUrl() {
+    return null;
+  }
+
+  @Override
   public long getKeystoreCacheSizeLimit() {
     return 0;
   }
diff --git a/knox-homepage-ui/home/app/homepage.service.ts b/knox-homepage-ui/home/app/homepage.service.ts
index 9a39218..c573140 100644
--- a/knox-homepage-ui/home/app/homepage.service.ts
+++ b/knox-homepage-ui/home/app/homepage.service.ts
@@ -16,6 +16,7 @@
  */
 import {Injectable} from '@angular/core';
 import {HttpClient, HttpErrorResponse, HttpHeaders} from '@angular/common/http';
+import {ActivatedRoute} from '@angular/router';
 import swal from 'sweetalert';
 
 import 'rxjs/add/operator/toPromise';
@@ -32,7 +33,7 @@ export class HomepageService {
     publicCertUrl = this.apiUrl + 'publicCert?type=';
     topologiesUrl = this.apiUrl + 'topologies';
 
-    constructor(private http: HttpClient) {}
+    constructor(private http: HttpClient, private route: ActivatedRoute) {}
 
     getGeneralProxyInformation(): Promise<GeneralProxyInformation> {
         let headers = new HttpHeaders();
@@ -128,6 +129,20 @@ export class HomepageService {
     }
 
     private handleError(error: HttpErrorResponse): Promise<any> {
+        //location.reload();
+        let refresh;
+        this.route.queryParams.subscribe(params => {
+          refresh = params['refresh'];
+          console.debug('refresh = ' + refresh)
+          if (refresh) {
+            console.debug('Refreshing page...', window.location.href);
+            var url = window.location.pathname.replace(new RegExp('refresh=1/.*'), '?');
+            //var url = window.location.pathname;
+            
+            //window.location.assign(url);
+            window.location.reload();
+          }
+        });
         swal('Oops!', 'Something went wrong!\n' + (error.error ? error.error : error.statusText), 'error');
         return Promise.reject(error.message || error);
     }
diff --git a/knox-homepage-ui/home/app/sessionInformation/session.information.component.ts b/knox-homepage-ui/home/app/sessionInformation/session.information.component.ts
index 8ac572c..497ea9c 100644
--- a/knox-homepage-ui/home/app/sessionInformation/session.information.component.ts
+++ b/knox-homepage-ui/home/app/sessionInformation/session.information.component.ts
@@ -35,10 +35,11 @@ export class SessionInformationComponent implements OnInit {
 
     getUser() {
         if (this.sessionInformation) {
-            console.debug('SessionInformationComponent --> getUser() --> ' + this.sessionInformation.user);
-            return this.sessionInformation.user;
+          console.debug('SessionInformationComponent --> getUser() --> ' + this.sessionInformation.user);
+          return this.sessionInformation.user;
         }
         console.debug('SessionInformationComponent --> getUser() --> dr.who');
+
         return 'dr.who';
     }
 
@@ -50,8 +51,15 @@ export class SessionInformationComponent implements OnInit {
         return null;
     }
 
+    getLogoutPageUrl() {
+        if (this.sessionInformation) {
+            console.debug('SessionInformationComponent --> getLogoutPageUrl() --> ' + this.sessionInformation.logoutPageUrl);
+            return this.sessionInformation.logoutPageUrl;
+        }
+        return null;
+    }
+
     logout() {
-        // window.alert('Are you sure???');
         console.debug('SessionInformationComponent --> attempting logout() --> ');
         if (this.sessionInformation) {
             if (!this.logoutSupported) {
@@ -59,7 +67,7 @@ export class SessionInformationComponent implements OnInit {
             }
             else {
                 this.homepageService.logout(this.getLogoutUrl())
-                                        .then(() => location.reload());
+                                        .then(() => window.location.assign(this.sessionInformation.logoutPageUrl));
             }
         }
     }
diff --git a/knox-homepage-ui/home/app/sessionInformation/session.information.ts b/knox-homepage-ui/home/app/sessionInformation/session.information.ts
index 183a9d4..549bb09 100644
--- a/knox-homepage-ui/home/app/sessionInformation/session.information.ts
+++ b/knox-homepage-ui/home/app/sessionInformation/session.information.ts
@@ -18,4 +18,6 @@
 export class SessionInformation {
     user: string;
     logoutUrl: string;
+    logoutPageUrl: string;
+    globalLgoutPageUrl: string;
 }
diff --git a/knox-homepage-ui/home/index.html b/knox-homepage-ui/home/index.html
index 4233428..431a388 100644
--- a/knox-homepage-ui/home/index.html
+++ b/knox-homepage-ui/home/index.html
@@ -18,6 +18,10 @@
     <meta charset="utf-8">
     <title>Apache Knox Home</title>
     <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
+    <meta http-equiv="Pragma" content="no-cache">
+    <meta http-equiv="Expires" content="0">
+    
     <link rel="icon" type="image/x-icon" href="favicon.ico">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <!-- Custom styles for this template -->