Resolving Your Identity Crisis with the Sun ONE Identity Server

   
   
Articles Index

Have you ever wondered how many passwords most people must keep track of? For example, in my case, all three of my banking institutions offer online access to my accounts, each with their own usernames and passwords. My four different credit card companies also provide online solutions, and I have at least three email accounts where I need to supply a password. And then there's my 401K provider, online storefronts, discussion forums, stock brokers, authentication at work to access employee databases, software repositories, utility bills, and on and on.

But usernames and passwords are not always sufficient to ensure security in today's information economy. The Internet has ushered in a myriad of web services that demand authentication infrastructures that are more sophisticated than traditional username/password schemes. Web services are required to interoperate and provide single sign-on facilities, and there are new requirements for service providers. Making users authenticate against a password file or a password table in a relational database (as was the case with legacy solutions) is just not going to cut it anymore.

Modern authentication architectures based on identity servers (supported by directory servers behind the scenes) attempt to address these drawbacks. One key step in this more modern approach is to migrate your data to a directory-based architecture -- and that is exactly what I will explore in this article.

I'll be using a demo program that authenticates by verifying username/passwords against a database table, as with traditional solutions. We'll then migrate the usernames and passwords in the database to the Sun ONE Identity Server's LDAP datastore, and modify our demo program to authenticate against the identity server. In the process, we will also explore the access management APIs of the identity server, and discuss how to integrate with the Sun ONE application servers.

Please note that this article is not a complete guide to architecting identity solutions.

Sun ONE Identity Server 6.0

The relational database is, by design, optimized for read/write performance, whereas identity servers are optimized only for high-performance read operations. The overhead required for transaction support may not be warranted for authentication. The hierarchical model of LDAP datastores is better suited for organizational data, without requiring complex SQL grammars. However, modern architectures based on identity/directory servers have a great deal to offer. In addition to the advantages mentioned in the previous paragraph, they also support single sign-on and the ability to federate across disparate business entities.

The Sun ONE software stack provides one such platform for authentication. The Sun ONE Identity Server 6.0 is a modern solution that encompasses federation, access management, administration services, support for such standard protocols as LDAP, RADIUS, X.509v3 certificates, single sign-on, and the JAAS (Java Authentication and Authorization Service) open standard framework for customizing authentication mechanisms.

The Demo Program

The demo program in our example is a JavaServer page (JSP page) that reads the username and password, and authenticates by verifying against the value stored in a relational database. The JSP page prompts for a username and password if those are not supplied in the request. Here is an overview of the program, called login.jsp (zip file):

username = request.getParameter("username");
password = request.getParameter("password");
if (username != null) { // verify username against database
        if (username OK) {
                // throw user OK message
        } else {
                // throw error message
        }
} else { // prompt for username
<form>
............
</form>
}

To modify this program to authenticate against the identity server, we must first create these users in the identity server. The user information will, of course, come from the database. Here is an overview of the program that will do just that:

for (each record in the username/password table) {
        read user information
        create the user in the identity server
}

The code reads every user record from the database and creates a corresponding entry in the identity server so that login.jsp can authenticate against the identity server.

Let's now modify login.jsp to authenticate against the identity server instead of the database. Note that creating users in the identity server and authenticating against the identity server must be done through the access management APIs (or AMAPI for short). The AMSDK (Access Management SDK) is bundled with the identity server.

Let's take a look at the AMAPI for creating users in the identity server.

// populate user attributes in a hashmap
           Map userAttributeMap = new HashMap();
           uid = UserName;
           storeUserAttributes("uid", uid, userAttributeMap);
           firstName = UserName;
           storeUserAttributes("givenname", firstName, userAttributeMap);
           lastName = UserName;
           storeUserAttributes("sn", lastName, userAttributeMap);
           passWord = Password;
           storeUserAttributes("userPassword", passWord, userAttributeMap);
           Map userMap1 = new HashMap();
           userMap1.put(uid, userAttributeMap);
           /**
            * Provide the DN according to the DIT
            */
           String dn = "ou=People,dc=Sun,dc=Com";
           AMPeopleContainer ampc = conn.getPeopleContainer(dn);
           ampc.createUsers(userMap1);

// Here is storeAttributes
        Set userSet = new HashSet();
        userSet.add(value);
        userMap.put(attribute, userSet);

Click here for the complete source for migrating user from the database to the identity server.

Here's the AMAPI for authenticating users in the identity server:

AuthContext AuthCtx = null;
                orgRoot = "dc=sun,dc=com"; // modify this to your organization
                try {
                        AuthCtx = new AuthContext(orgRoot);
                        Typ = AuthContext.IndexType.MODULE_INSTANCE;
                        Name = "LDAP";
                        System.out.println("obtained auth context");
                        AuthCtx.login(Typ, Name);
                } catch (LoginException le) {
                        le.printStackTrace();
                        System.out.println("could not obtain auth ctx !!");
                }

                Callback[] callbacks = null;
                while (AuthCtx.hasMoreRequirements()) {
                        callbacks = AuthCtx.getRequirements();
                        if (callbacks != null) {
                                int i = 0;
                                for (i = 0; i < callbacks.length; i++) {
........... // complete callback processing
                                }
                                AuthCtx.submitRequirements(callbacks);
                        }
                }

                if (AuthCtx.getStatus() == AuthContext.Status.SUCCESS) {
                        // login succeeded
                } else if (AuthCtx.getStatus() == AuthContext.Status.FAILED) {
                        // login failure
                }

The modified login page to authenticate against the identity server is available here (zip file).

Integrating with Sun ONE Application Servers

Instructions to run the demo are in the README file. Below are some caveats to keep in mind when integrating the AMAPI with Sun ONE Application Servers.

JSS Library (libjss.so)

Any application or module deployed on the Sun ONE Application Server version 7.0 must have access to the AMSDK libraries. (The AMSDK also includes some native libraries.) During my integration process, the appserver threw exceptions about not being able to load the libjss library when using the AMSDK. I had to copy the libjss3.so file to the application server's lib directory. Setting LD_LIBRARY_PATH to a path that contained the library didn't help (on Sun ONE Application Server 6.5). This is because the Iplanet Application Server 6.5 bundled version 2.1 of the libjss file (under its lib/jss directory), which is older than the 3.0 version of the same file bundled with the AMSDK. The exception was fixed when I copied the newer version of this library file to the application server's lib/jss..

Note: GUI console requires directory server restart when schema is modified. Sometimes, when I modified the schema by adding new attributes or object classes, the GUI console of the directory server did not seem to refresh it accurately. Instead, it seemed to require a shutdown and startup of the directory server -- as well as the console -- in order to recognize schema changes.

Conclusion

The LDAP datastore behind the scenes complements the identity server by way of access control lists (ACL), Class Of Service (CoS), and other features. The design of the LDAP schema is a whole topic in itself, and beyond the scope of this article. Developers and architects are encouraged to peruse further material on LDAP schema design and deployment. I believe this is essential to architecting a good identity solution.

Implementing a directory-based datastore for mostly read-only data is a good idea, in terms of performance as well design. Typically, organizational information doesn't change too often in the lifespan of an application. However, the Sun ONE Identity Server can also work off an RDBMS-based datastore. You should consider this if the migration proves to be an expensive affair. In the longer term, the directory-based approach is a more scalable solution.

See Also

Sun ONE Identity Server
Sun ONE Identity Server documentation
JAAS (Java Authentication and Authorization Service)

About the Author

Ezhilan Narasimhan is a member of the Market Development Engineering group at Sun Microsystems, Inc. He works with ISVs on performance engineering, database design, and application architecture.