This is a short guide to setting up Trusted Authentication into OIT's DocFinity product. As OIT doesn't support some of this functionality except as a services engagement from OIT, some of this information is not well documented. Consequently, this was put together using trial and error until I was able to get the SSO to work.
This was tested with version 10.11 of DocFinity.
In order to maintain a viable level of work in DocFinity, we would like to separate instances based on the type of work. In order to prevent users from having to log into multiple instances, a single sign-on application will need to be put together to act as a front-end to each instance.
Each DocFinity instance will have the users managed by a single authentication authority.
In order to maintain the highest security possible, the username and password will need to be authenticated with a source before being passed to the DocFinity Work Instances. The first step of the SSO application is to authenticate and encrypt the username from the core instance. This instance will need to have all of the users identified so that this will work for anyone who needs to access any of the instances.
A DocFinity administrator can change the names of the username and password parameters through Administration > System > System Properties > System > Authentication > trustedAuth.header.*
Parameter | Default Value |
---|---|
encryptedPassword | j_password_enc |
encryptedUsername | j_username_enc |
If you are looking to be able to authenticate with DocFinity without using the password, you need to enable trusted authentication. This not a setting that can be set through the admin system settings page, it needs to be done directly in the database. The SQL that you need to run is
UPDATE
SystemProperties
SET
value = 'true'
WHERE
name = 'trustedAuth.active'
This will allow you to authenticate by supplying only an encrypted username.
There are multiple modes for trusted authentication. The following table shows you how the requests will be processed based on various settings and parameters
TrustedAuth.Active Database Setting | Trusted IP | Username | Password | Authentication |
---|---|---|---|---|
True | Yes | plain text | passes | |
True | No | plain text | fails | |
True/False | Yes/No | plain text | plain text | passes |
True | No | encrypted | passes | |
True/False | Yes/No | encrypted | encrypted | passes |
If you are planning on using Trusted IP, you will need to update a properties file with the comma separated list of acceptable IP addresses.
<installDir>/OITAppServer/webapps/docfinity/WEB-INF/classes/config/common/security/spring-security.properties
trustedIps=127.0.0.1,192.168.0.5
The rest of this will primarily focus on encrypted username/passwords. If you are interested in Trusted IPs, please see your DocFinity Authentication Technical Guide or contact OIT.
There are two parameters that need to be included in the call - j_username_enc
and j_password_enc
. If you enabled Trusted Authentication then only j_username_enc
is needed. The url should look like this:
http://servername:port/docfinity/application/index.html?j_username_enc=encryptedusername&j_password_enc=encryptedpassword
Alternatively, you can use the new, mobile-friendly, URL API
http://servername:port/docfinity/urlapi/main//?j_username_enc=encryptedusername&j_password_enc=encryptedpassword
Unfortunately, there is no documentation on how to encrypt the username and password. I had to reverse engineer the URL API Assistant (see below) to figure out how to encrypt the username and password.
There is a REST API that is available that can be used to encrypt the username and password. Unfortunately, out of the box, the tomcat installation is not built to handle CORS requests. So the first step is to enable CORS on tomcat. I used the following site as a guide to enabling CORS in tomcat: http://tomcat.apache.org/tomcat-8.0-doc/config/filter.html#CORS_Filter
The TL;DR version is that you need to add the following to the web.xml of the docfinity app:
<!-- A filter to allow CORS for the Rest API -->
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.headers</param-name>
<param-value>Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/webservices/rest/*</url-pattern>
</filter-mapping>
You have to provide the cors.allowed.headers
init param because the default doesn't include Authorization
.
The URL to the encrypt service that can be used to get the encrypted username and password looks like this -
http://servername:port/docfinity/webservices/rest/ajax/encrypt?u=username&p=password
The REST service should be called using the POST
method and the Content-Type needs to be set to application/x-www-form-urlencoded; charset=UTF-8
. Additionally, it uses Basic Authentication to verify that you have permission to call the service. In order to support Basic Authentication, a header parameter needs to be added
Authorization: Basic Base64EncodedUsernameAndPassword
There are a couple ways to Base64 Encode the username and password. In modern browsers there is a built in function btoa() which accepts a string that it converts into Base64. In Internet Explorer 8 and lower, the btoa() function is not available; in which case you can specify your own equivalent implementation. The script below combines both methods. It will use btoa() if it is available, otherwise it will use the equivalent implmentation:
/**
*
* Base64 encode / decode
* http://www.webtoolkit.info/
*
* Modified to exit out to the browser capabilities if they are available.
*
**/
var Base64 = {
// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
// public method for encoding
encode : function (input) {
var output = "";
input = Base64._utf8_encode(input);
// check to see if btoa is available
if (typeof btoa == "function") {
output = btoa(input);
} else {
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
}
}
return output;
},
// public method for decoding
decode : function (input) {
// check to see if atob is available
var output = "";
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
// check to see if atob is available
if (typeof atob == "function") {
output = atob(input);
} else {
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
while (i < input.length) {
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
}
output = Base64._utf8_decode(output);
return output;
},
// private method for UTF-8 encoding
_utf8_encode : function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
},
// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
}
NOTE: This only works when you've enabled Trusted Authentication. If you have not enabled Trusted Authentication then don't select Send Trusted
as the type).
You can generate the URL that should be used to authenticate users using the URL API Assistant. The URL API Assistant simplifies the interaction, complication, and cryptic nature of URL API interaction. It assists with the creation of static URLs for entry into DocFinity's many key functionalities including interacting with batches, viewing documents, searching, interacting with BPM jobs, uploading files, using forms, and viewing dashboards.
The URL API Assistant is installed automatically with DocFinity and can be accessed via a web browser at the following URL:
http://servername:port/contextroot/urlapiassistant/
In the first section, the options allow you to specify the values necessary for authenticating into the DocFinity instance.
- Set the
Type
toSend Trusted
- Enter the username that you want to authenticate.
- At the bottom of the page, click on the button that says
Generate URL
Custom Logout Page
By default, when a user logs out of DocFinity, they are returned to the Login screen. Since users utilizing single sign on bypass the Login screen when they access DocFinity, seeing the Login screen may be confusing or seen as an error by some users. To avoid displaying the Login screen after logout, you can specify another page to display by changing the UI > Logout > custom.logout.url system property through System Administration.
Central Administration
When there are multiple instances of DocFinity, central administration can be used to administer all instances. How it is setup is unknown, but you can access it by going to the following URL:
http://servername:port/docfinity/application/index.html#module=central
Administrators must have the Central Administration Feature Right in the DocFinity instance that functions as the Central Administration entry point