Back | Next | Contents Cams Programmer's Guide

Cams JAAS Callback Handlers

This chapter describes how you write JAAS authentication callback handlers for use with the Cams server. If a standard Cams JAAS callback handler doesn't suit your needs, you can use this API to implement a new one.

Who Should Read this Document

Most web application programmers who use Cams to implement application security will not need to write a callback handler. Form authentication with usernames and passwords as provided by Cams is typically sufficient for most applications. In this case, the standard Cams NamePasswordCallbackHandler is used with the JdbcLoginModule, LdapLoginModule, and XmlLoginModule.

System administrators and programmers should read the Cams Administrator's Guide to understand how to configure Cams login modules with callback handlers.

This document is intended to assist experienced Java programmers with writing new Cams CallbackHandlers.

Related Documentation

This document provides terminology, descriptions, and programmatic examples specific to writing callback handlers for use with Cams. Though thorough within that scope, it is not intended to replace the abundance of contextual documentation available on JAAS, Java Security, and Pluggable Authentication. For more information, please see the links in the Resources section of Programing Cams LoginModule document.

Conceptual Overview

A Cams CallbackHandler is concerned with passing authentication credentials from the application to a login module. When a non-authenticated user makes a request for a protected HTTP resource, the Cams server redirects the user browser to display an HTML form known as a login page. The HTTP server that displays the page may be on the same server as the requested resource, or it may be on a different physical or logical server.

The login page usually requires that the user supply a username and password but may require other values, such as a pin, social security number, or even user transparent values such as digital certificates. The user populates the login page values and clicks submit. His credentials are sent to the Cams server, which sets the appropriate values in a Cams CallbackHandler using JavaBean setter notations. The LoginContext then passes the CallbackHandler to the configured login module for authentication. For more detailed information, see the Cams Authentication Pipeline document.

To create a new CallbackHandler, you implement the CallbackHandler interface. CallbackHandlers are application-dependent. For example, Cams web applications configured for HTML form authentication will display a browser page to prompt for requested information or to display error messages. Alternatively, a JFC client might popup a window to prompt the user for credentials. Future versions of Cams may include login modules and CallbackHandlers for use by JFC clients to enable single sign-on across JFC clients and web applications.

Create a New Callback Handler

The CallbackHandlers you create for use with Cams implement the standard Java CallbackHandler methods provided in the javax.security.auth.callback package. You must implement the handle method.

Source code is provided for CallbackHandlers included with the Cams distribution. You can use any Cams CallbackHandler as a starting point to make a new customized CallbackHandler. This section outlines how you might create a new CallbackHandler, named MyCallbackHandler, using the Cams NamePasswordCallbackHandler as a starting point.

  1. Copy the NamePasswordCallbackHandler
  2. Modify MyCallbackHandler
  3. Test MyCallbackHandler
  4. Deploy MyCallbackHandler

Copy the NamePasswordCallbackHandler

You should create MyCallbackHandler in a directory appropriate to your development environment. This can be any directory you like. Best practice suggests that you should use standard Java name space conventions to avoid conflicts. You can use com.cafesoft.cams.auth.callbacks.NamePasswordCallback.java as a template. After you've created MyCallbackHandler, open it in a text editor.

Modify MyCallbackHandler

You should change the package name to reflect the new file location. If you are only making a minor modification, you may not need to make any other changes to the imports. Otherwise, modify the imports as required.

package com.mycompany.callbacks;

MyCallbackHandler must implement the CallbackHandler interface, then declare (as Strings) the variables associated with each Callback. In the case of an Cams login page, the variable names should be the same as the HTML INPUT parameters used in the form, less the preceeding “cams_cb_” characters. For example, the login page values for the NamePasswordCallbackHandler variables shown in Example 1 would be cams_cb_username and cams_cb_password.

public class NamePasswordCallbackHandler implements CallbackHandler
{
	/**
	 * The username to be provided when prompted.
	 */
	private String username;

	/**
	 * The password to be provided when prompted.
	 */
	private String password;
Example 1 - Cams NamePasswordCallbackHandler declarations

Example 2 shows the NamePasswordCallbackHandler's no parameter default constructor. You'll need to create one for MyCallbackHandler. The Cams server dynamically instantiates a CallbackHandler for each authentication request by invoking the default constructor. You may create other constructors as required to facilitate, for example, automated testing.

public NamePasswordCallbackHandler()
{
	this.username = null;
	this.password = null;
}
Example 2 - The required Cams NamePasswordCallbackHandler constructor

Cams uses JavaBean setter notations to populate the required callback values. Authentication credentials are sent as name value pairs from the HTTP authorization proxy server to the Cams server. The authentication server uses introspection to set the corresponding values. Hence, you must create a setter for each callback type in MyCallbackHandler.

Each login configuration entry can have multiple login modules and multiple CallbackHandlers. You will need to support all of the callback types required by the login module's for which you configure MyCallbackHandler. The CallbackHandler's handle method is invoked by the configured login module to obtain callback values. It's really a controller that calls the appropriate methods to get the callback array values.

Example 3 is a complete listing of NamePasswordCallbackHandler code. As you can see, there's more to understanding the context than writing the code.

/**
 * Copyright (c) 1996-2007 Cafesoft, LLC. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Cafesoft, LLC. ("Confidential Information"). You shall not disclose
 * such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Cafesoft.
 *
 * CAFESOFT MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
 * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, OR NON-INFRINGEMENT. CAFESOFT SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
 * THIS SOFTWARE OR ITS DERIVATIVES.
 */
package com.cafesoft.cams.auth.callback;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;

import java.io.IOException;

/**
 * CallBack interface for JAAS LoginModules that must prompt for input. Provides
 * the specified username and password when prompted by the Callback.
 */
public class NamePasswordCallbackHandler implements CallbackHandler
{
	/**
	 * The username to be provided when prompted.
	 */
	private String username;

	/**
	 * The password to be provided when prompted.
	 */
	private String password;

	/**
	 * Create a new NamePasswordCallbackHandler (required Cams default 
	 * constructor that is dynamically called by the authentication
	 * server).
	 */
	public NamePasswordCallbackHandler()
	{
		this.username = null;
		this.password = null;
	}

	/**
	 * Create a new NamePasswordCallbackHandler (optional constructor
	 * used to facilitate testing).
	 *
	 * @param username the username to provide when prompted.
	 * @param password the password to provide when prompted.
	 */
	public NamePasswordCallbackHandler(String username, String password)
	{
		this.username = username;
		this.password = password;
	}

	/**
	 * Set the username.
	 *
	 * @param username the username to be provided to a NameCallback.
	 */
	public void setUsername(String username)
	{
		if (username == null)
		{
			this.username = "";
			return;
		}

		this.username = username;
	}

	/**
	 * Set the password.
	 *
	 * @param password the password to be provided to a PasswordCallback.
	 */
	public void setPassword(String password)
	{
		if (password == null)
		{
			this.password = "";
			return;
		}

		this.password = password;
	}

	/**
	 * Retrieve or display the information requested in the provided Callbacks. 
	 * The handle method implementation checks the instance(s) of the Callback 
	 * object(s) passed in to retrieve or display the requested information. 
	 *
	 * @param callbacks an array of Callback objects provided by an underlying 
	 * security service which contains the information requested to be retrieved 
	 * or displayed.  
	 *
	 * @exception IOException if an input or output error ocurrs
	 * @exception UnsupportedCallbackException if the implementation of this 
	 * method does not support one or more of the Callbacks specified in the 
	 * callbacks parameter 
	 */
	public void handle(Callback[] callbacks)
		throws IOException, UnsupportedCallbackException
	{
		// Loop over all Callbacks
		for (int i = 0; i < callbacks.length; i++)
		{
			Callback cb = callbacks[i];

			if (cb instanceof NameCallback)
			{
				((NameCallback)cb).setName(username);
			}
			else if (cb instanceof PasswordCallback)
			{
				// JAAS specifies that the password is a char[]
				((PasswordCallback)cb).setPassword(password.toCharArray());
			}
			else
			{
				throw new UnsupportedCallbackException(
					cb, "Unrecognized Callback");
			}
		}
	}
}  // End of class: NamePasswordCallbackHandler
Example 3 - The Cams NamePasswordCallbackHandler production code example

Congratulations, you're now ready to compile and test MyCallbackHandler.

Test MyCallbackHandler

To test MyCallbackHandler, you'll need to setup Cams on a development system and make sure that MyCallbackHandler is in the classpath (see the CAMS_HOME/bin/runcams.bat(sh) script). Also, you need to ensure that at least one security domain configuration is setup to use MyCallbackHandler. Please reference the Cams Administrator's Guide, which has complete instructions on configuring a login module and callback handler for use.

Deploy MyCallbackHandler

You can package MyCallbackHandler in a jar file with any other classes you may have made, or put the individual classes in a directory that is in the Cams server class path. See CAMS_HOME/bin/runcams.bat(sh) to configure the classpath.

Back | Next | Contents