Authentication Overview
While distributed applications offer many benefits to their users the development of such applications can be a complex process. The ability to correctly authenticate users has been a complex issue and has lead to the emergence of standard Authentication and Authorization frameworks, frameworks such as JAAS.
JAAS authentication is performed in a pluggable fashion. This permits applications to remain independent from underlying authentication technologies. New or updated authentication technologies can be plugged under an application without requiring modifications to the application itself.
Universal Messaging provides a wide variety of client APIs to develop enterprise, web and mobile applications. On the enterprise application front, Universal Messaging offers a transport protocol dependent authentication scheme while on the web and mobile application front a pluggable authentication framework is offered. The end result is that all applications can share the same Universal Messaging authorization scheme which requires a token@host based subject that access control lists can be defined upon.
Enterprise Application Authentication
Universal Messaging enterprise applications can be written in a variety of programming languages. Each one of these client APIs offers connectivity using one of the 4 available transport protocols, namely nsp (TCP Sockets), nhp (HTTP), nsps (SSL Sockets) and nhps (HTTPS). The authentication scheme is transport protocol dependent therefore providing a basic authentication scheme for TCP based transport protocols (nsp, nhp) and an SSL authentication scheme for SSL based transport protocols (nsps, nhps).
Basic Authentication Scheme
Under this mode of authentication the client passes the username to the server as part of the initial connection handshake. The server then extracts the remote host name and creates the subject to be used by this connection.
The client API can set the username component, however, the remote host is always set on the server. This stops clients from impersonating users from other hosts. The following diagram illustrates the basic authentication scheme's operation:
SSL Authentication Scheme
The Universal Messaging Realm server can be configured to perform Client Certificate authorization or to allow anonymous SSL clients to connect. When the server is configured to allow anonymous clients to connect the subject is built up based on the previous authentication method. That is the username portion is passed to it from the client.
When the server is configured for client certificate processing the subject is constructed with the Common Name (CN) of the certificate and the remote host name. This allows the ACLs to be configured such that not only is the certificate valid but it can only access the Realm Server from a specific host. The following diagram illustrates the SSL authentication scheme's operation when using client certificates:
Web Application Authentication
Universal Messaging web applications can use a pluggable authentication framework that presents its self as basic http authentication as defined by RFC 1945. Basic authentication is supported by all popular web browsers and users have to enter a username and password in a browser provided login dialog before proceeding. The web browser then automatically includes the token in the Authorization HTTP header for all subsequent requests to the server's authentication realm, for the lifetime of the browser process. Please note that although Universal Messaging supports basic authentication on both nhp (HTTP) and nhps (HTTPS) interfaces, it is only advised to use it over HTTPS connections to secure your web application against man in the middle attacks and network sniffing tools.
In order to host your web application on Universal Messaging, a number of server side plugins are provided that you can configure and mount on the various URLs that your application expects connections on. These are the XML plugin, the Servlet plugin, the SOAP plugin, the File plugin, the REST plugin and the Proxy Pass Through plugin.
Plugin Authentication Parameters
Each one of these plugins contains an identical set of configuration parameters that control its behavior towards authentication. These are described below:
Security Realm: Name of the authentication realm
AddUserAsCookie: Specifies if the authenticated username should be added as a cookie.
Authenticator: Fully qualified class name of authenticator to use, or blank to use the default implementation provided.
AuthParameters: A space delimited list of key=value definitions which are passed to the authenticator instance to initialize and configure it. These are passed to the Authenticator's init method.
GroupNames: An optional comma separated list of groups. The user must be a member of at least one group to be granted access even if a valid username/password is provided. The groups are dependent on the authenticator implementation.
RoleNames: An optional comma separated list of roles. The user must have at least one role to be granted access even if a valid username/password is provided. The roles are dependent on the authenticator implementation and are effectively the permissions defined.
ReloadUserFileDynamically: If set to true, the reload method of the authenticator implementation will be called prior to serving each http request. If set to false, the reload will only be called once when the Universal Messaging interface starts.
Common AuthParameters
Irrespective of the authenticator implementation you use in your Universal Messaging server plugins, there are some AuthParameters that are also used by the server. These are:
NamedInstance: This parameter requests that this authenticator configuration is bound to the specified named instance which will be shared across all plugins on this server that are configured to do so. Please note that the first plugin that accepts a connection will bind the name to the server together with the remaining configuration parameters. For this reason please make sure that configuration is always the same on all plugins that share the same instance.
Default Authenticator Implementation
Universal Messaging comes with a default authenticator implementation that uses a properties file to define users, groups and permissions (roles). In order to enable it on a Universal Messaging plugin, the Authenticator parameter needs to be left empty (this implies using the Default), the Authentication Realm set and one parameter needs to be set in AuthParameters.
The necessary parameter is called UserFile and should point to the full path of a java properties file, e.g. c:\users.txt. In order to get the Universal Messaging realm server to encrypt your user passwords, you need to add a property called initialize as shown below. This notifies the default authenticator that passwords are not encrypted so on the first load it will encrypt them, remove the initialize property and save your user file.
An example of a UserFile defining 3 permissions (roles), 3 groups and 3 users is shown below:
#Request password initialisation
initialise=true
#Permissions (Roles) Definition
perm_name_1=Guest
perm_name_2=User
perm_name_3=Admin
#Guests Group Definition
group_ID_Guests=1
group_desc_Guests=Guests Group
group_perm_Guests={1}
#Users Group Definition
group_ID_Users=2
group_desc_Users=Users Group
group_perm_Users={2}
#Admins Group Definition
group_ID_Admins=3
group_desc_Admins=Admins Group
group_perm_Admins={3}
#Example Guest User Definition
user_desc_someguest=Some Guest User
user_pass_someguest=password
user_perm_someguest={1}
user_home_id_someguest=Guests
user_group_someguest=Guests
#Example Regular User Definition
user_desc_someuser=Some User
user_pass_someuser=password
user_perm_someuser={1,2}
user_home_id_someuser=Users
user_group_someuser=Users
user_group_0_someuser=Guests
#Example Admin User Definition
user_desc_someadmin=Some Admin User
user_pass_someadmin=password
user_perm_someadmin={1,2,3}
user_home_id_someadmin=Admins
user_group_someadmin=Admins
user_group_0_someadmin=Guests
user_group_1_someadmin=Users
Custom Authenticator Implementations
The interface for creation of custom authenticator implementations is defined in the following 3 classes of the com.pcbsys.foundation.authentication package:
fAuthenticator: Represents the Authenticator Implementation and
has the following methods
public void init(Hashtable initParams);
public String getName();
public synchronized void close();
public void reload();
public fPermission addPermission(int permNo, String name) ;
public fUser addUser(String username, String description, String plainPassword,
String groupName);
public fUser copyUser(fUser user) ;
public fUser getUser(String username);
public void delUser(fUser user);
public fGroup addGroup(int id, String name, String description);
public fPermission getPermission(int id);
public fPermission getPermission(String name);
public fGroup getGroup(String name);
public void delPermission(int id);
public void delGroup(fGroup group);
public void saveState() throws IOException
fGroup: Represents the user groups and contains the following methods:
public void reload(int id, String name, String description);
public boolean isModified();
public void setModified(boolean flag);
public String getName();
public int getId();
public String getDescription();
public BitSet getPermissions();
public void addUser(fUser aUser);
public Enumeration getUsers();
public Hashtable getUserHash();
public void setUserHash(Hashtable newhash);
public void delUser(fUser aUser);
public int getNoUsers();
public void setPermission(fPermission perm);
public void clearPermission(fPermission perm);
public void resetPermission();
public BitSet getPermissionBitSet();
public boolean can(fPermission perm);
fUser : Represents the authentication users and has the following methods:
public void reload(String name, String description, String password,
fGroup group);
public void createUser(String name, String description, String password,
fGroup group) ;
public void setPassword(String pass);
public BitSet getPermissions();
public BitSet getTotalPermissions();
public boolean can(fPermission perm);
public String login(byte[] password, boolean requestToken, Hashtable params);
public String login(String password, boolean requestToken, Hashtable params);
public String getHomeId();
public void setHomeId(String myHomeId);
public void setGroup(fGroup group);
public void delGroup(fGroup group);
public String getName();
public String getDescription();
public String getPassword();
public fGroup getGroup();
public Enumeration getGroups();
public Hashtable getGroupHash();
public void setGroupHash(Hashtable newhash);
public int getNumGroups();
public void setPermission(fPermission perm);
public void setDescription(String desc);
public void clearPermission(fPermission perm);
public void setPermissionBitSet(BitSet newperms);
public BitSet getPermissionBitSet();
public void resetPermission();
public boolean isModified();
public void setModified(boolean flag);
Example Database Authenticator
As discussed in the previous section the default implementation is based on an optionally encrypted text file, with passwords being MD5 digested. It is however possible to use different storage mechanisms for users, groups and permissions such as a relational database. There are no restrictions on the design of the database schema as Universal Messaging simply needs a set of classes that comply to the fAuthenticator, fGroup and fUser interfaces. Please note that not all classes need to be subclassed but only the ones that you need to modify the default behaviour.
In the context of this example we are going to use a mysql database running on localhost and containing a users table with the following columns:
"Name": varchar
"Password": varchar
"Rights": int
"Home": varchar
In order to keep the example simple we are going to statically define the groups and permissions within the authenticator source code. We will use the group functionality on the base fGroup class and therefore will only subclass fAuthenticator and fUser as shown below:
DBAuthenticator
package com.myapp;
import com.pcbsys.foundation.authentication.*;
import com.pcbsys.nirvana.client.*;
import com.mysql.jdbc.Driver;
import java.io.*;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.DriverManager;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
public class DBAuthenticator extends fAuthenticator {
private static fGroup MYAPP_GROUP =null;
private static fGroup MYCOMPANY_GROUP =null;
protected static fPermission CLIENT_PERMISSION=null;
protected static fPermission ADMIN_PERMISSION=null;
private static fPermission GUEST_PERMISSION=null;
private boolean initialised=false;
private String myName="DBAuthenticator";
private static int myUniqueID=0;
private static Connection myConnection;
private static String jdbcurl = "jdbc:mysql://localhost:3306/test";
private static String myDBUser="root";
private static String myDBPassword="";
//Lets statically define the groups and permissions
static {
//Company Group
MYCOMPANY_GROUP =new fGroup();
MYCOMPANY_GROUP.reload(2,"mycompany", "MyCompany Group");
//Application Group
MYAPP_GROUP =new fGroup();
MYAPP_GROUP.reload(0,"mycompany/myapp", "MyApp Group");
GUEST_PERMISSION=new fPermission();
GUEST_PERMISSION.reload(0,"Guest");
CLIENT_PERMISSION=new fPermission();
CLIENT_PERMISSION.reload(1,"Client");
ADMIN_PERMISSION=new fPermission();
ADMIN_PERMISSION.reload(4,"Admin");
}
public void close(){
super.close();
if(getUsageCount() == 0){
fAuthenticator.logAuthenticatorMessage("{"+getName()+"} "+
"Closing Authenticator ["+getUsageCount()+"]");
//release connection pool
if (myConnection!=null){
try {
myConnection.close();
} catch (SQLException e) {}
myConnection=null;
}
initialised=false;
}
else {
fAuthenticator.logAuthenticatorMessage("{"+getName()+"} "+
"Closing Authenticator ["+getUsageCount()+"]");
}
}
public DBAuthenticator() {
super();
addGroup(MYCOMPANY_GROUP);
addGroup(MYAPP_GROUP);
getPermissionsCollection().put("Client", CLIENT_PERMISSION);
getPermissionsCollection().put("Admin", ADMIN_PERMISSION);
getPermissionsCollection().put("Guest", GUEST_PERMISSION);
}
protected static Connection getDBConnection() throws SQLException{
if (myConnection==null){
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
myConnection = DriverManager.getConnection(jdbcurl,myDBUser,
myDBPassword);
} catch (InstantiationException e) {
e.printStackTrace();
//To change body of catch statement use
// File | Settings | File Templates.
} catch (IllegalAccessException e) {
e.printStackTrace();
//To change body of catch statement use
//File | Settings | File Templates.
} catch (ClassNotFoundException e) {
e.printStackTrace();
//To change body of catch statement use
// File | Settings | File Templates.
}
}
return myConnection;
}
public String getName() {
return myName;
}
private static String getNextId() {
return ""+myUniqueID++;
}
public void init(Hashtable initParams) throws IOException {
if (!initialised){
if (initParams.containsKey("NamedInstance")){
myName=(String)initParams.get("NamedInstance");
}
else {
myName=myName+"_"+getNextId();
fAuthenticator.logAuthenticatorMessage("{"+getName()+"} "+
"Default Instance Requested ");
}
if (initParams.get("DBUser")!=null){
myDBUser=(String)initParams.get("DBUser");
}
if (initParams.get("DBPassword")!=null){
myDBPassword=(String)initParams.get("DBPassword");
}
if (initParams.get("JDBCURL")!=null){
jdbcurl=(String)initParams.get("JDBCURL");
}
initialised=true;
}
}
private DBUser createDBUserInstance(String username, String description,
String password, int permissions, int home){
DBUser someuser=new DBUser();
someuser.setAuthenticator(this);
if (home==1){ //MyCompany/MyApp Users
someuser.reload(username,description,password, MYAPP_GROUP);
//Set home
someuser.setHomeId(MYAPP_GROUP.getName());
//Group association logic
someuser.setGroup(MYAPP_GROUP);
//Set outer group
someuser.setGroup(MYCOMPANY_GROUP);
MYCOMPANY_GROUP.addUser(someuser);
//Set inner group
MYAPP_GROUP.addUser(someuser);
//Permissions association logic
switch (permissions){
case 1:
someuser.setPermission(CLIENT_PERMISSION);
break;
case 10:
someuser.setPermission(ADMIN_PERMISSION);
break;
default:{
someuser.setPermission(GUEST_PERMISSION);
break;
}
}
}
else {
fAuthenticator.logAuthenticatorMessage("WARNING: User "+username+
" has a home value of "+home+". User will be ignored!");
return null;
}
return someuser;
}
private DBUser LoadUserFromDatabase(String username){
DBUser someuser=null;
Connection conn =null;
java.sql.Statement stmt = null;
java.sql.ResultSet rset=null;
String name=null;
try{
conn=getDBConnection();
stmt = conn.createStatement();
rset = stmt.executeQuery(
"select name,password, rights,home from USERS where name='"+
username.toLowerCase()+"'" );
while(rset.next())
{
int permissions= rset.getInt("RIGHTS");
int home = rset.getInt("HOME"); // home desk association
name=rset.getString("name").toLowerCase();
String password=rset.getString("password");
if (password==null || password.trim().length()==0 ||
password.equals("null")) password="nopassword";
String description="A "+name+" user";
//safeguard for users without a description!
someuser=createDBUserInstance(name,description,password,
permissions,home);
if (someuser==null) continue;
//In case we have invalid data to create this user object
//Cache instance
getUsersCollection().put(someuser.getName(),someuser);
}
rset.close();
rset=null;
stmt.close();
stmt=null;
}
catch (Throwable t){
logAuthenticatorException(
"DBAuthenticator: Error obtaining details for user "+name);
logAuthenticatorException(t);
t.printStackTrace();
}
finally {
if (rset!=null){
try {
rset.close();
} catch (SQLException e) {
}
rset=null;
}
if (stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
}
stmt=null;
}
}
return someuser;
}
private void LoadUsersFromDatabase(){
Connection conn =null;
java.sql.Statement stmt=null;
java.sql.ResultSet rset=null;
String name=null;
try{
conn=getDBConnection();
stmt = conn.createStatement();
rset = stmt.executeQuery(
"select name,password, rights,home from USERS order by name" );
while(rset.next())
{
int rights= rset.getInt("RIGHTS");
int home = rset.getInt("HOME"); // home desk association
name=rset.getString("name").toLowerCase();
String password=rset.getString("password");
if (password==null || password.trim().length()==0 ||
password.equals("null")) password="nopassword";
String description="A "+name+" user";
DBUser someuser=createDBUserInstance(name,description,password,
rights,home);
if (someuser==null) continue;
getUsersCollection().put(someuser.getName(),someuser);
}
rset.close();
rset=null;
stmt.close();
stmt=null;
}
catch (Throwable t){
logAuthenticatorException("Error obtaining details for user "+name);
logAuthenticatorException(t);
t.printStackTrace();
}
finally {
if (rset!=null){
try {
rset.close();
} catch (SQLException e) {
}
rset=null;
}
if (stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
}
stmt=null;
}
}
}
public void reload() throws IOException {
LoadUsersFromDatabase();
fAuthenticator.logAuthenticatorMessage("{"+getName()+"} "+
"Reload called");
}
/**
* Creates a new fPermission with the unique ID and name supplied.
*
* The implementation should save the new permission to the relevant
* technology used.
*
* @param permNo Unique ID from 0 to 63.
* @param name Name describing this new permission.
* @return the new fPermission.
* @throws java.io.IOException If unable to create the new fPermission.
*/
public fPermission addPermission(int permNo, String name) throws
IOException {
if(getPermissionsCollection().get(""+permNo) == null){
fPermission perm = new fPermission();
perm.reload(permNo,name);
getPermissionsCollection().put(""+permNo, perm);
return perm;
}
fAuthenticator.logAuthenticatorMessage("{"+getName()+"} "+
"Added Permission "+name+"("+permNo+")");
return (fPermission) super.getPermissionsCollection().get(""+permNo);
}
public fUser addUser(String username, String description,
String plainPassword, String groupName) throws IOException {
fGroup group = null;
if (groupName != null) {
group = (fGroup) getGroupsCollection().get(groupName);
if (group == null) throw new IOException("No known group " + groupName);
}
fUser user = createUser(username, description, plainPassword, group);
getUsersCollection().put(user.getName(), user);
if(group != null) group.addUser(user);
fAuthenticator.logAuthenticatorMessage("{"+getName()+"} "+"Added User "+
username+" NOTE: This is not currently persisted in the database!");
return user;
}
/**
* Creates a new fUser with the supplied values.
* The password field is passed as plain text but it is up to the
* implementation to ensure the password
* is secure.
*
* The implementation should save the new user to the relevant
* technology used.
*
* @param user The user to copy.
* @return The new fUser created.
* @throws java.io.IOException If there where any errors during the
* construction of the user.
*/
public fUser copyUser(fUser user) throws IOException {
fGroup group = null;
group = (fGroup) getGroupsCollection().get(user.getGroup().getName());
fUser aUser = createUser(user.getName(),user.getDescription(),
user.getPassword(),user.getGroup());
getUsersCollection().put(aUser.getName(), aUser);
if(group != null) group.addUser(aUser);
fAuthenticator.logAuthenticatorMessage("{"+getName()+"} "+
"Copied User "+user.getName());
return aUser;
}
/**
* Adds a new group with the supplied values.
*
* The implementation should save the new group to the relevant
* technology used.
*
* @param id Unique ID for the group.
* @param name Name of the new group.
* @param description Description of the new group.
* @return The new fGroup object.
* @throws java.io.IOException If unable to create the new fGroup object.
*/
public fGroup addGroup(int id, String name, String description) throws
IOException {
fGroup group = new fGroup();
group.reload(id, name, description);
addGroup(group);
fAuthenticator.logAuthenticatorMessage("{"+getName()+"} "+
"Added Group "+group.getName());
return group;
}
/**
* Returns the permission with the ID supplied or null if not found.
*
* @param id fPermission Id to search for.
* @return the fPermission or null if not found.
*/
public fPermission getPermission(int id) {
Enumeration perms = getPermissionsCollection().elements();
while (perms.hasMoreElements()) {
fPermission fPermission = (fPermission) perms.nextElement();
if (fPermission.getId() == id) return fPermission;
}
return null;
}
/**
* Returns the permission with the name supplied or null if not found.
*
* @param name fPermission name to search for.
* @return the fPermission or null if not found.
*/
public fPermission getPermission(String name) {
return (fPermission)getPermissionsCollection().get(name);
}
public Enumeration getUsers(){
return getUsersCollection().elements();
}
public fUser getUser(String username) {
return (fUser) LoadUserFromDatabase(username.toLowerCase());
}
public fGroup getGroup(String name) {
return (fGroup)getGroupsCollection().get(name);
}
/**
* Removes the permission with the ID supplied.
*
* The implementation should remove the permission from the relevant
* technology used.
*
* @param id of the permission to delete.
* @throws java.io.IOException if unable to delete the permission.
*/
public void delPermission(int id) throws IOException {
getPermissionsCollection().remove(""+id);
fAuthenticator.logAuthenticatorMessage("{"+getName()+"} "+
"Deleted permission ("+id+")");
}
/**
* Removes the user supplied.
*
* The implementation should remove the user from the relevant
* technology used.
*
* @param user fUser object to remove.
* @throws java.io.IOException If unable to remove the user.
*/
public void delUser(fUser user) throws IOException {
if (user.getGroup() != null) {
user.getGroup().delUser(user);
}
getUsersCollection().remove(user.getName());
fAuthenticator.logAuthenticatorMessage("{"+getName()+"} "+
"Deleted User "+user.getName());
}
/**
* Removes the supplied fGroup object.
*
* Any user currently a member of this group will have the group reset
* to null meaning no group membership.
*
* The implementation should remove the group from the relevant
* technology used.
*
* @param group Group to remove.
* @throws java.io.IOException If unable to remove the group.
*/
public void delGroup(fGroup group) throws IOException {
Enumeration enm = group.getUsers();
while (enm.hasMoreElements()) {
fUser user = (fUser) enm.nextElement();
user.delGroup(group);
}
getGroupsCollection().remove(group.getName());
fAuthenticator.logAuthenticatorMessage("{"+getName()+"} "+
"Deleted Group "+group.getName());
}
/**
* Requests that the implementation save the current state.
*
* This should include all users, groups and permissions.
*
* @throws java.io.IOException if the save failed.
*/
public void saveState() throws IOException {
//TODO: Implement saving of data to the database
}
public void roll() throws IOException {
//TODO: Implement any log file rolling
}
protected fUser createUser(String name, String desc, String password,
fGroup group){
// System.out.println("CreateUSer being called");
DBUser usr = new DBUser();
usr.reload(name, desc, password, group);
usr.setHomeId(group.getName());
usr.setAuthenticator(this);
return usr;
}
}
DBUser
package com.myapp;
import com.pcbsys.foundation.authentication.fUser;
import com.pcbsys.foundation.authentication.fGroup;
import com.pcbsys.foundation.authentication.fAuthenticator;
import java.util.Hashtable;
import java.sql.SQLException;
public class DBUser extends fUser {
private static fAuthenticator myAuthenticator;
protected DBUser(){
super();
super.setGroupHash(new Hashtable());
}
//Allow setting a reference to the authenticator instance so that we can
// obtain its DB connection for
// user authentication purposes
public static void setAuthenticator (fAuthenticator authenticator){
myAuthenticator=authenticator;
}
protected DBUser(String name, String desc, String password){
this(name, desc, password, null);
}
protected DBUser(String name, String desc, String password, fGroup group){
super(name,desc,password,group);
}
public String login(byte[] password, boolean requestToken){
return login(password,requestToken,null);
}
public String login(String password, boolean requestToken){
return login(password,requestToken,null);
}
public String login(byte[] password, boolean requestToken, Hashtable params){
return login(new String(password), requestToken, params);
}
public String login(String password, boolean requestToken, Hashtable params){
java.sql.Connection conn =null;
java.sql.Statement stmt=null;
java.sql.ResultSet rset=null;
String name=null;
try{
conn=((DBAuthenticator)myAuthenticator).getDBConnection();
stmt = conn.createStatement();
rset = stmt.executeQuery(
"select password, rights from USERS where name ='"+
this.getName()+"'" );
while(rset.next())
{
int rights= rset.getInt("RIGHTS");
String thepassword=rset.getString("password");
if (thepassword.equals(password)){
if (rights > 0) return "true";
}
}
rset.close();
rset=null;
stmt.close();
stmt=null;
}
catch (Throwable t){
myAuthenticator.logAuthenticatorException(
"DBAuthenticator: Error obtaining details for user "+name);
myAuthenticator.logAuthenticatorException(t);
}
finally {
if (rset!=null){
try {
rset.close();
} catch (SQLException e) {
}
rset=null;
}
if (stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
}
stmt=null;
}
}
return null;
}
}
Web Application Single Sign On
Single sign-on (SSO) is a method of access control that enables a user to log in once and gain access to the required application and its resources without being prompted to log in again.When developing multi node Universal Messaging web applications, you have to take into consideration that incidents such a network failure, could cause the user's browser to fail over to a different node than the one initially authenticated with. In order to prevent the application user from authenticating again, or to integrate your Universal Messaging web application to a 3d party authentication mechanism and provide alternative authentication user interfaces, you can use Single Sign On Interceptors (SSI).
A Single Sign On Interceptor (SSI) is a class that conforms to a specific interface and gets invoked by the Universal Messaging realm prior to authentication in order to decide which of the following 3 outcomes should occur:
If the user meets the criteria required, allow access to the plugin content as if they where normally authenticated, optionally generating a unique session id.
If the user does not meet the criteria required, but a redirection is configured, redirect their browsers to the specified URL in order to authenticate.
If the user does not meet the criteria required, and no redirection is configured, then fall back to the regular authenticator configured.
The interface for creation of a nirvana SSI implementations is defined in the following 2 classes of the com.pcbsys.foundation.authentication package:
fSSInterceptor
//Return an fSSUser with a null username to fall back to authenticator
// (or redirect if a URL is set)
public abstract fSSUser getSSUser(Hashtable httpHeaders,
Hashtable urlParameters);
public abstract void setParameters(Hashtable params);
public abstract void clear();
public abstract String getName();
fSSUser
public fSSUser(String username, String redirectURL);
public fSSUser(String username, String redirectURL, String token);
public String getUsername();
public String getRedirectURL();
public String getToken()
Plugin Single Sign On Interceptor Parameters
SSInterceptor: Fully qualified class name of SSI to use. If not specified, no interceptor will be used
SSOAppendToken: Setting this parameter to true instructs the SSI object to generate and return a unique session ID when an affirmative single sign on decision is reached. Please note that the absence of a session ID is irrelevant to the single sign on decision.
Common Single Sign On AuthParameters
Irrespective of the Single Sign On implementation you use in your Universal Messaging server plugins, there are some AuthParameters that are also used by the server. These are:
SSONamedInstance: This optional parameter requests that this SSI object is bound to the specified named instance which will be shared across all plugins on this server that are configured to do so. Please note that the first plugin that accepts a connection will bind the name to the server together with the remaining configuration parameters. For this reason please make sure that configuration is always the same on all plugins that share the same SSI instance.
REDIRECT_URL: This optional parameter specifies the URL that a web client should be redirected to should the interceptor's criteria are not met. This allows the creation of alternative authentication methods such as form based authentication or others.
Mobile Application Authentication
When developing Universal Messaging based mobile applications, authentication is dependent on your mobile technology of choice. This is because the Universal Messaging Mobile APIs work exactly like any Universal Messaging enterprise or web API (with the exception of SSL client certificates).