svn commit: r1852884 - in /ofbiz/ofbiz-framework/branches/release18.12: ./ framework/common/webcommon/WEB-INF/ framework/security/config/ framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ themes/common-theme/webapp/common/js/util/

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r1852884 - in /ofbiz/ofbiz-framework/branches/release18.12: ./ framework/common/webcommon/WEB-INF/ framework/security/config/ framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ themes/common-theme/webapp/common/js/util/

mbrohl
Author: mbrohl
Date: Mon Feb  4 09:26:42 2019
New Revision: 1852884

URL: http://svn.apache.org/viewvc?rev=1852884&view=rev
Log:
Applied fix from trunk for revision: 1852882
===

Fixed: Error parsing JWT
(OFBIZ-10814)

Fixes incorrect retrieval of the Authorization header JWT token.
Fixes wrong API usage for the key parameter which assumed the key is
provided in BAS64 format.
Refactored the code to use helper methods for key and auth header
retrieval.
Javadoc corrections and enhancements.

Modified:
    ofbiz/ofbiz-framework/branches/release18.12/   (props changed)
    ofbiz/ofbiz-framework/branches/release18.12/framework/common/webcommon/WEB-INF/common-controller.xml
    ofbiz/ofbiz-framework/branches/release18.12/framework/security/config/security.properties
    ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ExternalLoginKeysManager.java
    ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/JWTManager.java
    ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/TokenFilter.java
    ofbiz/ofbiz-framework/branches/release18.12/themes/common-theme/webapp/common/js/util/OfbizUtil.js

Propchange: ofbiz/ofbiz-framework/branches/release18.12/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Feb  4 09:26:42 2019
@@ -10,4 +10,4 @@
 /ofbiz/branches/json-integration-refactoring:1634077-1635900
 /ofbiz/branches/multitenant20100310:921280-927264
 /ofbiz/branches/release13.07:1547657
-/ofbiz/ofbiz-framework/trunk:1849931,1850015,1850023,1850530,1850647,1850685,1850694,1850711,1850914,1850918,1850921,1850948,1850953,1851006,1851013,1851068,1851074,1851130,1851158,1851200,1851224,1851247,1851254,1851315,1851319,1851350,1851353,1851433,1851500,1851805,1851885,1851998,1852503,1852587,1852818
+/ofbiz/ofbiz-framework/trunk:1849931,1850015,1850023,1850530,1850647,1850685,1850694,1850711,1850914,1850918,1850921,1850948,1850953,1851006,1851013,1851068,1851074,1851130,1851158,1851200,1851224,1851247,1851254,1851315,1851319,1851350,1851353,1851433,1851500,1851805,1851885,1851998,1852503,1852587,1852818,1852882

Modified: ofbiz/ofbiz-framework/branches/release18.12/framework/common/webcommon/WEB-INF/common-controller.xml
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/branches/release18.12/framework/common/webcommon/WEB-INF/common-controller.xml?rev=1852884&r1=1852883&r2=1852884&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/branches/release18.12/framework/common/webcommon/WEB-INF/common-controller.xml (original)
+++ ofbiz/ofbiz-framework/branches/release18.12/framework/common/webcommon/WEB-INF/common-controller.xml Mon Feb  4 09:26:42 2019
@@ -31,7 +31,7 @@ under the License.
         <event name="checkRequestHeaderLogin" type="java" path="org.apache.ofbiz.webapp.control.LoginWorker" invoke="checkRequestHeaderLogin"/>
         <event name="checkServletRequestRemoteUserLogin" type="java" path="org.apache.ofbiz.webapp.control.LoginWorker" invoke="checkServletRequestRemoteUserLogin"/>
         <event name="checkExternalLoginKey" type="java" path="org.apache.ofbiz.webapp.control.ExternalLoginKeysManager" invoke="checkExternalLoginKey"/>
-        <event name="checkJWTLogin" type="java" path="org.apache.ofbiz.webapp.control.ExternalLoginKeysManager" invoke="checkJWTLogin"/>
+        <event name="checkJWTLogin" type="java" path="org.apache.ofbiz.webapp.control.JWTManager" invoke="checkJWTLogin"/>
         <event name="checkProtectedView" type="java" path="org.apache.ofbiz.webapp.control.ProtectViewWorker" invoke="checkProtectedView"/>
         <event name="extensionConnectLogin" type="java" path="org.apache.ofbiz.webapp.control.LoginWorker" invoke="extensionConnectLogin"/>
     </preprocessor>

Modified: ofbiz/ofbiz-framework/branches/release18.12/framework/security/config/security.properties
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/branches/release18.12/framework/security/config/security.properties?rev=1852884&r1=1852883&r2=1852884&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/branches/release18.12/framework/security/config/security.properties (original)
+++ ofbiz/ofbiz-framework/branches/release18.12/framework/security/config/security.properties Mon Feb  4 09:26:42 2019
@@ -143,3 +143,10 @@ login.secret_key_string=Secret Key
 
 # -- Time To Live of the token send to the external server in seconds, 10 seconds seems plenty enough OOTB. Custom projects might want set a lower value.
 security.jwt.token.expireTime=10
+
+# -- Enables the internal Single Sign On feature which allows a token based login between OFBiz instances
+# -- To make this work you also have to configure a secret key with security.token.key
+security.internal.sso.enabled=false
+
+# -- The secret key for the JWT token signature. Configuration in the SystemProperty entity is recommended for security reasons.
+#security.token.key=

Modified: ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ExternalLoginKeysManager.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ExternalLoginKeysManager.java?rev=1852884&r1=1852883&r2=1852884&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ExternalLoginKeysManager.java (original)
+++ ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ExternalLoginKeysManager.java Mon Feb  4 09:26:42 2019
@@ -18,29 +18,22 @@
  */
 package org.apache.ofbiz.webapp.control;
 
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
 import org.apache.ofbiz.base.util.Debug;
 import org.apache.ofbiz.entity.Delegator;
 import org.apache.ofbiz.entity.DelegatorFactory;
-import org.apache.ofbiz.entity.GenericEntityException;
 import org.apache.ofbiz.entity.GenericValue;
-import org.apache.ofbiz.entity.util.EntityQuery;
 import org.apache.ofbiz.service.LocalDispatcher;
-import org.apache.ofbiz.service.ModelService;
 import org.apache.ofbiz.webapp.WebAppUtil;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
 /**
- * This class manages the authentication tokens that provide single sign-on authentication to the OFBiz applications.
+ * This class manages the single sign-on authentication through external login keys between OFBiz applications.
  */
 public class ExternalLoginKeysManager {
     private static final String module = ExternalLoginKeysManager.class.getName();
@@ -105,11 +98,10 @@ public class ExternalLoginKeysManager {
      * The method is designed to be used in a chain of controller preprocessor event: it always return "success"
      * even when the authentication token is missing or the authentication fails in order to move the processing to the
      * next event in the chain.
-
      *
      * @param request - the http request object
      * @param response - the http response object
-     * @return - &amp;success&amp; in all the cases
+     * @return "success" in all the cases
      */
     public static String checkExternalLoginKey(HttpServletRequest request, HttpServletResponse response) {
         String externalKey = request.getParameter(EXTERNAL_LOGIN_KEY_ATTR);
@@ -145,6 +137,10 @@ public class ExternalLoginKeysManager {
                 // ignore the return value; even if the operation failed we want to set the new UserLogin
             }
 
+            // check userLogin base permission and if it is enabled
+            request.getSession().setAttribute("userLogin", userLogin);
+            userLogin = LoginWorker.checkLogout(request, response);
+
             LoginWorker.doBasicLogin(userLogin, request);
 
             // Create a secured cookie with the correct userLoginId
@@ -160,141 +156,13 @@ public class ExternalLoginKeysManager {
         return "success";
     }
 
-    private static boolean isAjax(HttpServletRequest request) {
-       return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
-    }
-
     /**
-    * OFBiz controller preprocessor event
-    * The method is designed to be used in a chain of controller preprocessor event: it always return "success"
-    * even when the Authorization token is missing or the Authorization fails.
-    * This in order to move the processing to the next event in the chain.
-    *
-    * This works in a similar same way than externalLoginKey but between 2 servers on 2 different domains,
-    * not 2 webapps on the same server.
-    *  
-    * The Single Sign On (SSO) is ensured by a JWT token,
-    * then all is handled as normal by a session on the reached server.
-    *  
-    * The servers may or may not share a database but the 2 loginUserIds must be the same.
-    *
-    * In case of a multitenancy usage, the tenant is verified.
-    * @param request The HTTPRequest object for the current request
-    * @param response The HTTPResponse object for the current request
-    * @return String "success"
-    */
-    public static String checkJWTLogin(HttpServletRequest request, HttpServletResponse response) {
-        Delegator delegator = (Delegator) request.getAttribute("delegator");
-
-        Map<String, Object> result = null;
-        String authorizationHeader = request.getHeader("Authorization");
-        if (authorizationHeader == null) {
-            // No Authorization header, no need to continue, most likely case.
-            return "success";
-        }
-
-        result = jwtValidation(delegator, authorizationHeader);
-        if (result.containsKey(ModelService.ERROR_MESSAGE)) {
-            // The JWT is wrong somehow, stop the process, details are in log
-            return "success";
-        }
-
-        GenericValue userLogin = getUserlogin(delegator, result);
-        if (userLogin == null) {
-            // No UserLogin GenericValue could be retrieved, stop the process, details are in log
-            return "success";
-        }
-
-        checkTenant(request, response, delegator, userLogin);
-
-        if (!storeUserlogin(userLogin)) {
-            // We could not store the UserLogin GenericValue (very unlikely), stop the process, details are in log
-            return "success";
-        }
-
-        LoginWorker.doBasicLogin(userLogin, request);
-        return "success";
-    }
-
-    /**
-     * Checks it's the right tenant in case username and password are the same in different tenants
-     * If not, sets the necessary session attributes
-     * @param request The HTTPRequest object for the current request
-     * @param response The HTTPResponse object for the current request
-     * @param delegator The current delegator
-     * @param userLogin The GenericValue object of userLogin to check
+     * Checks if the request is an Ajax request.
+     * @param request the request to check
+     * @return indicator
      */
-    private static void checkTenant(HttpServletRequest request, HttpServletResponse response, Delegator delegator,
-            GenericValue userLogin) {
-        //
-        //
-
-        LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
-        String oldDelegatorName = delegator.getDelegatorName();
-        ServletContext servletContext = request.getSession().getServletContext();
-        if (!oldDelegatorName.equals(userLogin.getDelegator().getDelegatorName())) {
-            delegator = DelegatorFactory.getDelegator(userLogin.getDelegator().getDelegatorName());
-            dispatcher = WebAppUtil.makeWebappDispatcher(servletContext, delegator);
-            LoginWorker.setWebContextObjects(request, response, delegator, dispatcher);
-        }
-    }
-
-    /**
-     * Stores the userLogin in DB. If it fails log an error message
-     * @param userLogin The userLogin GenericValue to store
-     * @return boolean True if it works, log an error message if it fails
-     */
-    private static boolean storeUserlogin(GenericValue userLogin) {
-        String enabled = userLogin.getString("enabled");
-        if (enabled == null || "Y".equals(enabled)) {
-            userLogin.set("hasLoggedOut", "N");
-            try {
-                userLogin.store();
-            } catch (GenericEntityException e) {
-                Debug.logError(e, "Cannot store UserLogin information: " + e.getMessage(), module);
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Gets the userLogin from the userLoginId in the result of the JWT validation
-     * If it fails, log a warning or error message  
-     * @param delegator The current delegator
-     * @param jwtMap Map of name, value pairs composing the result of the JWT validation
-     * @return userLogin The userLogin GenericValue extracted from DB
-     */
-    private static GenericValue getUserlogin(Delegator delegator, Map<String, Object> jwtMap) {
-        String userLoginId = (String) jwtMap.get("userLoginId");
-        GenericValue userLogin = null;
-        try {
-            userLogin = EntityQuery.use(delegator).from("UserLogin").where("userLoginId", userLoginId).queryOne();
-            if (userLogin == null) {
-                Debug.logWarning("*** There was a problem with the JWT token. Could not find userLogin " + userLoginId, module);
-            }
-        } catch (GenericEntityException e) {
-            Debug.logError(e, "Cannot get UserLogin information: " + e.getMessage(), module);
-        }
-        return userLogin;
+    private static boolean isAjax(HttpServletRequest request) {
+       return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
     }
 
-    /**
-     * Validate the token usingJWTManager::validateToken
-     * If it fails, returns a ModelService.ERROR_MESSAGE in the result
-     * @param delegator The current delegator
-     * @param authorizationHeader The JWT which normally contains the userLoginId
-     * @param result  Map of name, value pairs composing the result
-     */
-    private static Map<String, Object> jwtValidation(Delegator delegator, String authorizationHeader) {
-        Map<String, Object> result;
-        List<String> types = Arrays.asList("userLoginId");
-        result = JWTManager.validateToken(delegator, authorizationHeader, types);
-        if (result.containsKey(ModelService.ERROR_MESSAGE)) {
-            // Something unexpected happened here  
-            Debug.logWarning("*** There was a problem with the JWT, not signin in the user login ", module);
-        }        
-        return result;
-    }
-    
 }

Modified: ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/JWTManager.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/JWTManager.java?rev=1852884&r1=1852883&r2=1852884&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/JWTManager.java (original)
+++ ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/JWTManager.java Mon Feb  4 09:26:42 2019
@@ -18,59 +18,133 @@
  */
 package org.apache.ofbiz.webapp.control;
 
-import java.util.Calendar;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.ofbiz.base.util.Debug;
-import org.apache.ofbiz.base.util.UtilDateTime;
-import org.apache.ofbiz.base.util.UtilHttp;
-import org.apache.ofbiz.base.util.UtilMisc;
-import org.apache.ofbiz.base.util.UtilValidate;
+import io.jsonwebtoken.*;
+import org.apache.ofbiz.base.util.*;
 import org.apache.ofbiz.entity.Delegator;
+import org.apache.ofbiz.entity.DelegatorFactory;
+import org.apache.ofbiz.entity.GenericEntityException;
 import org.apache.ofbiz.entity.GenericValue;
+import org.apache.ofbiz.entity.util.EntityQuery;
 import org.apache.ofbiz.entity.util.EntityUtilProperties;
 import org.apache.ofbiz.service.GenericServiceException;
 import org.apache.ofbiz.service.LocalDispatcher;
 import org.apache.ofbiz.service.ModelService;
 import org.apache.ofbiz.service.ServiceUtil;
+import org.apache.ofbiz.webapp.WebAppUtil;
 
-import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.ExpiredJwtException;
-import io.jsonwebtoken.JwtBuilder;
-import io.jsonwebtoken.Jwts;
-import io.jsonwebtoken.SignatureAlgorithm;
-import io.jsonwebtoken.SignatureException;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.HttpHeaders;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
- * This class manages the authentication tokens
+ * This class manages the single sign-on authentication through JWT tokens between OFBiz applications.
  */
 public class JWTManager {
     private static final String module = JWTManager.class.getName();
 
+    /**
+     * OFBiz controller preprocessor event.
+     *
+     * The method is designed to be used in a chain of controller preprocessor event: it always returns "success"
+     * even when the Authorization token is missing or the Authorization fails.
+     * This in order to move the processing to the next event in the chain.
+     *
+     * This works in a similar same way than externalLoginKey but between 2 servers on 2 different domains,
+     * not 2 webapps on the same server.
+     *
+     * The OFBiz internal Single Sign On (SSO) is ensured by a JWT token,
+     * then all is handled as normal by a session on the reached server.
+     *
+     * The servers may or may not share a database but the 2 loginUserIds must be the same.
+     *
+     * In case of a multitenancy usage, the tenant is verified.
+     * @param request The HTTPRequest object for the current request
+     * @param response The HTTPResponse object for the current request
+     * @return String  always "success"
+     */
+    public static String checkJWTLogin(HttpServletRequest request, HttpServletResponse response) {
+        Delegator delegator = (Delegator) request.getAttribute("delegator");
+
+        if(!"true".equals(EntityUtilProperties.getPropertyValue("security", "security.internal.sso.enabled", "false", delegator))) {
+            if(Debug.infoOn()) {
+                Debug.logInfo("Internal single sign on is disabled.", module);
+            }
+            return "success";
+        }
+
+        // we are only interested in the header entry "Authorization" containing "Bearer <token>"
+        String jwtToken = getHeaderAuthBearerToken(request);
+        if (jwtToken == null) {
+            // No Authorization header, no need to continue.
+            return "success";
+        }
+
+        Map<String, Object> claims = validateJwtToken(jwtToken, getJWTKey(delegator));
+        if (claims.containsKey(ModelService.ERROR_MESSAGE)) {
+            // The JWT is wrong somehow, stop the process, details are in log
+            return "success";
+        }
+
+        // get userLoginId from the token and retrieve the corresponding userLogin from the database
+        GenericValue userLogin = getUserlogin(delegator, claims);
+
+        if(UtilValidate.isNotEmpty(userLogin)) {
+            // check userLogin base permission and if it is enabled
+            request.getSession().setAttribute("userLogin", userLogin);
+            userLogin = LoginWorker.checkLogout(request, response);
+        }
+
+        if (userLogin == null) {
+            // No UserLogin GenericValue could be retrieved, stop the process, details are in log
+            return "success";
+        }
+
+        checkTenant(request, response, delegator, userLogin);
+
+        if (!storeUserlogin(userLogin)) {
+            // We could not store the UserLogin GenericValue (very unlikely), stop the process, details are in log
+            return "success";
+        }
+
+        LoginWorker.doBasicLogin(userLogin, request);
+        return "success";
+    }
+
+    /**
+     * Get the JWT secret key from database or security.properties.
+     * @param delegator the delegator
+     * @return the JWT secret key
+     */
+    public static String getJWTKey(Delegator delegator) {
+        return EntityUtilProperties.getPropertyValue("security", "security.token.key", delegator);
+    }
+
      /**
      * Get the authentication token based for user
-     * This takes OOTB username/password and if user is authenticated it will generate JJWT token using secreate key
+     * This takes OOTB username/password and if user is authenticated it will generate the JWT token using a secret key.
      *
-     * @param request - the http request in which the authentication token is searched and stored
+     * @param request the http request in which the authentication token is searched and stored
      * @return the authentication token
      */
-
     public static String getAuthenticationToken(HttpServletRequest request, HttpServletResponse response){
         LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
         Delegator delegator = (Delegator) request.getAttribute("delegator");
-        String username = request.getParameter("USERNAME");
-        String password = request.getParameter("PASSWORD");
+
+        String username, password;
 
         if (UtilValidate.isNotEmpty(request.getAttribute("USERNAME"))) {
             username = (String) request.getAttribute("USERNAME");
+        } else {
+            username = request.getParameter("USERNAME");
         }
         if (UtilValidate.isNotEmpty(request.getAttribute("PASSWORD"))) {
             password = (String) request.getAttribute("PASSWORD");
+        } else {
+            password = request.getParameter("PASSWORD");
         }
 
         if (UtilValidate.isEmpty(username) || UtilValidate.isEmpty(password)) {
@@ -93,67 +167,86 @@ public class JWTManager {
         }
         GenericValue userLogin = (GenericValue) result.get("userLogin");
 
-        String token = createJwt (delegator, UtilMisc.toMap("userLoginId", userLogin.getString("userLoginId")));
+        String token = createJwt(delegator, UtilMisc.toMap("userLoginId", userLogin.getString("userLoginId")));
         if (token == null) {
             Debug.logError("Unable to generate token", module);
             request.setAttribute("_ERROR_MESSAGE_", "Unable to generate token");
             return "error";
         }
-        request.setAttribute("token",token);
+        request.setAttribute("token", token);
         return "success";
     }
 
-    /* This method will be used to validate token,
-     * If token is valid it will get the claims and return
-     * If token validation failed it will return error
+    /**
+     * Gets the authentication token from the "Authorization" header if it is in the form "Bearer <token>".
+     * Public for API access from third party code.
      *
-     * @param delegator
-     * @param token
-     * @param types  List of string that will be extracted from token claims if found
-     * @param result  Map of name, value pairs composing the result
+     * @param request the request to get the token from
+     * @return the bare JWT token
+     */
+    public static String getHeaderAuthBearerToken(HttpServletRequest request){
+
+        String headerAuthValue = request.getHeader(HttpHeaders.AUTHORIZATION);
+        String bearerPrefix = "Bearer ";
+
+        if(UtilValidate.isEmpty(headerAuthValue) || !headerAuthValue.startsWith(bearerPrefix)) {
+            return null;
+        }
+
+        // remove prefix and any leading/trailing spaces and return the bare token
+        return headerAuthValue.replaceFirst(bearerPrefix, "").trim();
+    }
+
+    /* Validates the provided token using the secret key.
+     * If the token is valid it will get the conteined claims and return them.
+     * If token validation failed it will return an error.
+     * Public for API access from third party code.
+     *
+     * @param token the JWT token
+     * @param key the server side key to verify the signature
+     * @return Map of the claims contained in the token
      */
-    public static Map<String, Object> validateToken(Delegator delegator, String token, List<String> types) {
+    public static Map<String, Object> validateToken(String jwtToken, String key) {
         Map<String, Object> result = new HashMap<String, Object>();
-        if (UtilValidate.isEmpty(token)) {
-            Debug.logError("Token can not be empty", module);
-            result.put(ModelService.ERROR_MESSAGE, "Token can not be empty.");
+        if (UtilValidate.isEmpty(jwtToken) || UtilValidate.isEmpty(key)) {
+            String msg = "JWT token or key can not be empty.";
+            Debug.logError(msg, module);
+            result.put(ModelService.ERROR_MESSAGE, msg);
             return result;
         }
         try {
-            String key = EntityUtilProperties.getPropertyValue("security", "security.token.key", "ofbiz", delegator);
-            Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();
+            Claims claims = Jwts.parser().setSigningKey(key.getBytes()).parseClaimsJws(jwtToken).getBody();
             //OK, we can trust this JWT
-            for (int i = 0; i < types.size(); i++) {
-                result.put(types.get(i), claims.get(types.get(i)));
-            }
-            return result;
-        } catch (SignatureException e) {
-            //don't trust the JWT!
-            Debug.logError(e.getMessage(), module);
-            result.put(ModelService.ERROR_MESSAGE, e.getMessage());
+            result.putAll(claims);
             return result;
-        } catch (ExpiredJwtException e) {
-            //Token Expired: Ask for login again.
+        } catch (SignatureException | ExpiredJwtException e) {
+            // signature not valid or token expired
             Debug.logError(e.getMessage(), module);
             result.put(ModelService.ERROR_MESSAGE, e.getMessage());
             return result;
         }
     }
 
-    public static String createJwt(Delegator delegator, Map<String, String> tokenMap) {
+    /**
+     * Create and return a JWT token using the claims of the provided map and the configured expiration time.
+     * @param delegator the delegator
+     * @param claims the map containing the JWT claims
+     * @return a JWT token
+     */
+    public static String createJwt(Delegator delegator, Map<String, String> claims) {
         int expirationTime = Integer.parseInt(EntityUtilProperties.getPropertyValue("security", "security.jwt.token.expireTime", "1800",  delegator));
-        return createJwt(delegator, tokenMap, expirationTime);
+        return createJwt(delegator, claims, expirationTime);
     }
 
-    /* Generate and return a JWT key
+    /* Create and return a JWT token using the claims of the provided map and the provided expiration time.
      *
      * @param delegator
-     * @param tokenMap Map name, value pairs to set as claims
+     * @param tokenMap the map containing the JWT claims
      * @param expireTime the expiration time in seconds
      * @return a JWT token
      */
-    public static String createJwt (Delegator delegator, Map<String, String> claims, int expireTime) {
-        String key = EntityUtilProperties.getPropertyValue("security", "security.token.key", "ofbiz", delegator);
+    public static String createJwt(Delegator delegator, Map<String, String> claims, int expireTime) {
+        String key = JWTManager.getJWTKey(delegator);
 
         Calendar cal = Calendar.getInstance();
         cal.setTimeInMillis(UtilDateTime.nowTimestamp().getTime());
@@ -162,11 +255,93 @@ public class JWTManager {
         JwtBuilder builder = Jwts.builder()
                 .setExpiration(cal.getTime())
                 .setIssuedAt(UtilDateTime.nowTimestamp())
-                .signWith(SignatureAlgorithm.HS512, key);
+                .signWith(SignatureAlgorithm.HS512, key.getBytes());
 
         for (Map.Entry<String, String> entry : claims.entrySet()) {
             builder.claim(entry.getKey(), entry.getValue());
         }
         return builder.compact();
     }
+
+    /**
+     * Checks it's the right tenant in case username and password are the same in different tenants
+     * If not, sets the necessary session attributes
+     * @param request The HTTPRequest object for the current request
+     * @param response The HTTPResponse object for the current request
+     * @param delegator The current delegator
+     * @param userLogin The GenericValue object of userLogin to check
+     */
+    private static void checkTenant(HttpServletRequest request, HttpServletResponse response, Delegator delegator,
+            GenericValue userLogin) {
+
+        String oldDelegatorName = delegator.getDelegatorName();
+        ServletContext servletContext = request.getSession().getServletContext();
+        if (!oldDelegatorName.equals(userLogin.getDelegator().getDelegatorName())) {
+            delegator = DelegatorFactory.getDelegator(userLogin.getDelegator().getDelegatorName());
+            LocalDispatcher dispatcher = WebAppUtil.makeWebappDispatcher(servletContext, delegator);
+            LoginWorker.setWebContextObjects(request, response, delegator, dispatcher);
+        }
+    }
+
+    /**
+     * Stores the userLogin in DB. If it fails log an error message
+     * @param userLogin The userLogin GenericValue to store
+     * @return boolean True if it works, log an error message if it fails
+     */
+    private static boolean storeUserlogin(GenericValue userLogin) {
+        String enabled = userLogin.getString("enabled");
+        if (enabled == null || "Y".equals(enabled)) {
+            userLogin.set("hasLoggedOut", "N");
+            try {
+                userLogin.store();
+            } catch (GenericEntityException e) {
+                Debug.logError(e, "Cannot store UserLogin information: " + e.getMessage(), module);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Gets the userLogin from the userLoginId in the result of the JWT validation
+     * If it fails, log a warning or error message
+     * @param delegator The current delegator
+     * @param jwtMap Map of name, value pairs composing the result of the JWT validation
+     * @return userLogin The userLogin GenericValue extracted from DB
+     */
+    private static GenericValue getUserlogin(Delegator delegator, Map<String, Object> jwtMap) {
+        String userLoginId = (String) jwtMap.get("userLoginId");
+
+        if(UtilValidate.isEmpty(userLoginId)) {
+            Debug.logWarning("No userLoginId found in the JWT token.", module);
+            return null;
+        }
+
+        GenericValue userLogin = null;
+        try {
+            userLogin = EntityQuery.use(delegator).from("UserLogin").where("userLoginId", userLoginId).queryOne();
+            if (userLogin == null) {
+                Debug.logWarning("There was a problem with the JWT token. Could not find provided userLogin " + userLoginId, module);
+            }
+        } catch (GenericEntityException e) {
+            Debug.logError(e, "Cannot get UserLogin information: " + e.getMessage(), module);
+        }
+        return userLogin;
+    }
+
+    /**
+     * Validate the token usingJWTManager::validateToken
+     * If it fails, returns a ModelService.ERROR_MESSAGE in the result
+     * @param jwtToken The JWT which normally contains the userLoginId
+     * @param key the secret key to decrypt the token
+     * @return Map of name, value pairs composing the result
+     */
+    private static Map<String, Object> validateJwtToken(String jwtToken, String key) {
+        Map<String, Object> result = validateToken(jwtToken, key);
+        if (result.containsKey(ModelService.ERROR_MESSAGE)) {
+            // Something unexpected happened here
+            Debug.logWarning("There was a problem with the JWT token, no single sign on user login possible.", module);
+        }
+        return result;
+    }
 }

Modified: ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/TokenFilter.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/TokenFilter.java?rev=1852884&r1=1852883&r2=1852884&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/TokenFilter.java (original)
+++ ofbiz/ofbiz-framework/branches/release18.12/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/TokenFilter.java Mon Feb  4 09:26:42 2019
@@ -18,23 +18,8 @@
  */
 package org.apache.ofbiz.webapp.control;
 
-import java.io.IOException;
-import java.util.Locale;
-import java.util.Map;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
 import org.apache.ofbiz.base.util.Debug;
 import org.apache.ofbiz.base.util.UtilHttp;
-import org.apache.ofbiz.base.util.UtilMisc;
 import org.apache.ofbiz.base.util.UtilProperties;
 import org.apache.ofbiz.base.util.UtilValidate;
 import org.apache.ofbiz.common.CommonEvents;
@@ -44,6 +29,15 @@ import org.apache.ofbiz.entity.GenericVa
 import org.apache.ofbiz.entity.util.EntityQuery;
 import org.apache.ofbiz.service.ModelService;
 import org.apache.ofbiz.webapp.WebAppUtil;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.util.Locale;
+import java.util.Map;
+
 public class TokenFilter implements Filter  {
     public static final String module = TokenFilter.class.getName();
 
@@ -61,10 +55,10 @@ public class TokenFilter implements Filt
         Delegator delegator = WebAppUtil.getDelegator(config.getServletContext());
         Locale locale = UtilHttp.getLocale(httpRequest);
 
-        String token = httpRequest.getHeader("Bearer");
+        String token = JWTManager.getHeaderAuthBearerToken(httpRequest);
 
         if (UtilValidate.isNotEmpty(token)) {
-            Map<String, Object> result = JWTManager.validateToken(delegator, token, UtilMisc.toList("userLoginId"));
+            Map<String, Object> result = JWTManager.validateToken(token, JWTManager.getJWTKey(delegator));
             String userLoginId = (String) result.get("userLoginId");
             if (UtilValidate.isNotEmpty(result.get(ModelService.ERROR_MESSAGE))) {
                 httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

Modified: ofbiz/ofbiz-framework/branches/release18.12/themes/common-theme/webapp/common/js/util/OfbizUtil.js
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/branches/release18.12/themes/common-theme/webapp/common/js/util/OfbizUtil.js?rev=1852884&r1=1852883&r2=1852884&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/branches/release18.12/themes/common-theme/webapp/common/js/util/OfbizUtil.js (original)
+++ ofbiz/ofbiz-framework/branches/release18.12/themes/common-theme/webapp/common/js/util/OfbizUtil.js Mon Feb  4 09:26:42 2019
@@ -1349,7 +1349,7 @@ function sendJWT(targetUrl) {
             async: false,
             type: 'POST',
             xhrFields: {withCredentials: true},
-            headers: {"Authorization" : jwtToken},
+            headers: {"Authorization" : "Bearer " + jwtToken},
             success: function(){
                 window.location.assign(redirectUrl);
             }