Note: Now that we have learned to pass username and password from the URL, in this blog we will learn how to do encryption and decryption of authentication details for enhanced security. For configuration details, please follow the link provided below and specifically Step-4:
How External Application is used to interact with HIIntroduction
Product companies who want to embed Helical Insight, they may not want to use the user role management of Helical Insight since they are already managing it inside their product. Custom Authentication refers to the ability of an application to be authenticated through the credentials of another application. And also, the authentication is a process that happens with encrypted token which holds parameters such as:
- Username
- Password
- Organization
- Role etc.,
This means, if two separate applications each having its own authentication process is linked together, then authentication through one application also authenticates the second application.
This type of granting access is useful when embedding one application into another or integrating two or more applications together.
Custom authentication in Helical Insight
Helical Insight can be integrated with other applications in multiple ways, and if need be, can allow custom authentication process to authenticate the users of the parent application and give access to the Dashboards visualized in Helical Insight.
To enable custom authentication, there is a two part process to be followed:
- Configuring Parent Application to send the authentication details.
- Configuring Helical Insight to access and validate the authentication details.
Configuring Parent Application to send the authentication details
The parent application will create an encrypted token which contains the following parameters separated by a pipe (‘|’) in the URL.
- username – name of the user accessing the application
- password
- org_name – organization name of the user
- role – role of the user
Example of pipelining above mentioned parameters to create an encrypted token:
authToken=username=user|password=user|org_name=userOrg|role=ROLE_USER
authToken is a parameter that contains all the parameters and their respective values which are encrypted.
To create an authToken, in the backend an algorithm is used which is AES (Advanced Encryption Standard). This algorithm supports a block length of 128 bits and key lengths of 128, 196 and 256 bits.
Every encryption and decryption algorithm needs certain parameters to encrypt and decrypt passed parameter and their values at both ends. And the algorithm parameters are:
- cipherAlgorithm – Algorithm used to encrypt and decrypt the authToken. The algorithm used to do the same is AES (Advanced Encryption Standard).
- cipherMode – Every encrypted token (authToken) is divided into certain blocks and each block is encrypted separately. Cipher mode used to do the same is ECB (Electronic Codebook).
- cipherPadding – Each block that is created by cipher mode should be of fixed size and if any of the block is not of the same size then extra bits will be added in that block. This is called padding. Cipher padding used to do the same is PKCS5Padding.
- cipherKey – It is the cipher key used for both encryption and decryption.
The above mentioned algorithm parameters are found in the file mentioned below:
../customAuthentication.properties
Above file is present in Cipher.jar.
Path to Cipher.jar in application:
/{HI Application}/{apache-tomcat}/webapps/hi-ee/WEB-INF/lib
The jar file which is used for encryption and decryption is gwt-crypto-2.3.0. It is a Google Web Toolkit application running over Google App Engine.
For encryption and decryption, classes of gwt-crypto-2.3.0.jar are used. Below code is used for encryption with the parameters that are imported from customAuthentication.properties file.
Path to above Cipher.jar:
/{HI Application}/{apache-tomcat}/webapps/hi-ee/WEB-INF/lib
Encryption class:
This class is found in CipherUtils.class file in Cipher.jar.
public static String encrypt(String strToEncrypt) throws IOException { try { input = CipherUtils.class.getResourceAsStream("/customAuthentication.properties"); // load a properties file prop.load(input); key = prop.getProperty("cipherKey"); algorithm = prop.getProperty("cipherAlgorithm"); mode = prop.getProperty("cipherMode"); padding = prop.getProperty("cipherPadding"); Cipher cipher = Cipher.getInstance(algorithm+"/"+mode+"/"+padding); final SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), algorithm); cipher.init(Cipher.ENCRYPT_MODE, secretKey); final String encryptedString = Base64.encodeBase64URLSafeString(cipher.doFinal(strToEncrypt.getBytes())); return encryptedString; } catch (Exception e) { e.printStackTrace(); } input.close(); return null; }
In the above code, properties that are obtained from the customAuthentication.properties file are referenced to variables with key, algorithm, mode and padding.
Then a secret key is constructed using SecretKeySpec. This constructor takes two parameters. They are:
- key – the key material of the secret key (cipherKey)
- algorithm – the name of the secret-key algorithm (cipherAlgorithm)
To initialize cipher to encryption on secret key, ENCRYPTION_MODE is used. And then secret key is encoded using Base64 to create a string and return it.
Decryption class:
public static String decrypt(String strToDecrypt) { try { input = CipherUtils.class.getResourceAsStream("/customAuthentication.properties"); // load a properties file prop.load(input); key = prop.getProperty("cipherKey"); algorithm = prop.getProperty("cipherAlgorithm"); mode = prop.getProperty("cipherMode"); padding = prop.getProperty("cipherPadding"); Cipher cipher = Cipher.getInstance(algorithm+"/"+mode+"/"+padding); final SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), algorithm); cipher.init(Cipher.DECRYPT_MODE, secretKey); final String decryptedString = new String(cipher.doFinal(Base64.decodeBase64(strToDecrypt))); return decryptedString; } catch (Exception e) { e.printStackTrace(); } return null; }
In decryption, all the custom authentication properties are needed (custom authentication properties used for encryption) and then cipher mode is changed to DECRYPT_MODE and then Base64 is decoded and returned.
checkIfExists method in customUserDetailService class
Below method is found in encryptedAuthentication.jar and in customUserDetailService.class file. Path to above mentioned jar:
/{HI Application}/{apache-tomcat}/webapps/hi-ee/WEB-INF/lib
checkIfExists method:
public void checkIfExists(HttpServletRequest request) { String encryptedToken = request.getParameter("authToken"); String token = CipherUtils.decrypt(encryptedToken); logger.info("Token Data = " + token); String[] userDetailsParams = token.split(Pattern.quote("|")); Map<String, String> map = new HashMap(); for (int i = 0; i < userDetailsParams.length; i++) { String[] detail = userDetailsParams[i].split(Pattern.quote("=")); map.put(detail[0], detail[1]); } String combinedUsername = (String)map.get("username") + ":" + (String)map.get("org_name"); UserDetails userDetails = this.userService.loadUserByUsername(combinedUsername); request.setAttribute("userName", combinedUsername); request.setAttribute("password", map.get("password")); }
Above method will decrypt the authToken received with request and splits the token which is formed using pipe (refer Configuring Parent Application to send the authentication details). After decrypting, mapping is done with the received username, org_name and stored in a string called combinedUserName. And then parameters are set as attributes with the request as userName and password.
customDBAuthenticationFilter class
Below class is found in encryptedAuthentication.jar and in customDBAuthenticationFilter.class file. Path to above mentioned jar:
/{HI Application}/{apache-tomcat}/webapps/hi-ee/WEB-INF/libCode:
public class CustomDBAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private static final Logger logger = LoggerFactory.getLogger(CustomDBAuthenticationFilter.class); public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { logger.info("Request Data DBAuth = " + request); try { CustomUserDetailService cu = (CustomUserDetailService)ApplicationContextAccessor.getBean(CustomUserDetailService.class); cu.checkIfExists(request); } catch (Exception e) { logger.error("{}This is an error in CustomDBAuthenticationFilter", e); e.printStackTrace(); } return super.attemptAuthentication(request, response); } protected String obtainUsername(HttpServletRequest request) { return (String)request.getAttribute("userName"); } protected String obtainPassword(HttpServletRequest request) { return (String)request.getAttribute("password"); } }
In the above class file, attributes that are written from customAuthenticationFilter.class file are received and condition check is applied whether the received attributes are present in the database are not. That is username and password attributes that are set in the checkIfExists (refer: checkIfExists method:) method. If authentication fails then an exception is thrown.
Authenticate session
Below method is found in encryptedAuthentication.jar and in customAuthenticationFilter.class file. Path to above mentioned jar:
/{HI Application}/{apache-tomcat}/webapps/hi-ee/WEB-INF/lib
isAuthentication method:
protected boolean isAuthentication(HttpServletRequest request) { boolean authenticate; String token = request.getParameter("authToken"); if (token == null) { authenticate = false; } else { Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); if (existingAuth != null && existingAuth.isAuthenticated()) { authenticate = false; } else { authenticate = !isUrlExcluded(request); } } return authenticate; }
This method returns a Boolean value (true / false). The authToken which is created is taken from HTTP request and kept it in a variable called token. If the token (authToken) is null then authentication gets failed which means that the user cannot login.
If once authentication token that is received from HTTP request then that token is used as reference for that particular session and also user doesn’t need to provide authToken for further requests. For this, the following code snippet is used (taken from above method).
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); if (existingAuth != null && existingAuth.isAuthenticated()) { authenticate = false; }
Till now we have covered how to encrypt, decrypt, authenticate, and check if authToken exists in the session, algorithm used for encryption and decryption and code details of the same.
Now let us create a token (authToken):
Code snippet:
public class tokenizer { public static void main(String[] args){ String encryptedData =encryptor("username=hiuser|password=hipwd|org_name=hitesting|role=ROLE_USER"); System.out.println(encryptedData); String decryptedData = decryptor(encryptedData); System.out.println(decryptedData); } public static String encryptor(String data){ String encryptedData = null; try { encryptedData = CipherUtils.encrypt(data); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return encryptedData; } public static String decryptor(String data){ String decryptedData = CipherUtils.decrypt(data); return decryptedData; } }
In CipherUtils.class file, encrypt and decrypt are two methods used for encryption decryption respectively. In the above class, those two methods are used for encryption first with the provided arguments and then decrypt the same.
Arguments that are passed are hardcoded as seen in the above class:
String encryptedData =encryptor("username=hiuser|password=hipwd|org_name=hitesting|role=ROLE_USER");
When above class is saved and run, a token will be returned which is used as authToken.
Encrypted token:
authToken=ow4135Dn-pnSzE76BrphJQeeDj91rTZqh4dDFHilqdvKs7Oki6pNiLZL8w9wFws4_ocSMyFGnYP2bF4E7oJE1nik0I6tX-l9d4grdOtU2Xg
Now this generated authToken is used in URL along with directory and file details to for custom authentication.
Example:
{baseURL}/hi-ee/hi.html?dir={directory path}&file={file name}&authToken=ow4135Dn-pnSzE76BrphJQeeDj91rTZqh4dDFHilqdvKs7Oki6pNiLZL8w9wFws4_ocSMyFGnYP2bF4E7oJE1nik0I6tX-l9d4grdOtU2Xg
Note:
- Jar files that are used for encrypted authentication should be available in the following location
- This blog post only emphasizes on encryption and decryption of authentication details. For configuration details, please follow the link provided below and specifically Step-4: How External Application is used to interact with Helical Insight
{HI Application}/{apache-tomcat}/webapps/hi-ee/WEB-INF/lib