2.4 Understanding the Authentication Class Example

This section demonstrates how a password authentication class might be implemented by using the PasswordClass. All authentication classes are derived from the LocalAuthenticationClass, so you need to understand the key methods within it:

2.4.1 Extending the Base Authentication Class

Authentication classes extend the base class LocalAuthenticationClass as shown on lines 11 and 12 of PasswordClass Example Code. The LocalAuthenticationClass has a single constructor that must be called as shown in lines 20 - 23. The Identity Server uses this constructor to pass the necessary properties and user store information defined in the Administration Console to the class.

The LocalAuthenticationClass defines a single abstract method, doAuthenticate(), which must be implemented by new classes. During user authentication, the Identity Server creates an instance of an authentication class and calls the authenticate() method, which in turn calls the doAuthenticate() method. By default, the class instance remains persistent, allowing the state to be preserved between requests/responses while credentials are obtained. If persistence is not needed, the mustPersist() method can be overloaded to return False so new instances of the class are created upon each call to the authenticate() method.

2.4.2 Implementing the doAuthenticate Method

Lines 43 - 65 in the PasswordClass Example Code show how the doAuthenticate() method is used. Return values from this method indicate to the Identity Server that the class has succeeded or failed to authenticate a user or that additional user credentials are required and must be obtained.

The call to the isFirstCallAfterPrevMethod() method on line 49 determines if the call to the class is following a successful authentication by another class executed by a method. If that is the case, any credentials provided for the previous class most likely are not valid for this class and should not be tested for (line 52). In this example, the handlePostedData() method is called to obtain and validate a username and password entered by a user.

2.4.3 Prompting for Credentials

When lines are encountered in the PasswordClass Example Code, it has been determined that a page needs to be returned through the execution of a JSP to enable credentials to be prompted for and returned. Tests are made to determine if provisioning should be enabled, and if a Cancel button and federated providers should be displayed. The return value of HANDLED_REQUEST or SHOW_JSP indicates that the class has responded to the request and requires more information to proceed.

2.4.4 Verifying Credentials

The handlePostedData() method does much of the important work of this example (lines 74 - 114 in the PasswordClass Example Code). Lines 81 - 100 attempt to obtain the credentials.

Line 86 provides an example of obtaining a class property configured by an administrator. In this case, a query can be defined by the administrator that can be used to look up a user instead of using the username and password. If the query is used, the authenticateWithQuery method is called at line 88. If a query is not available, the authenticateWithPassword() method is called at line 98.

If the credentials correctly identify the user, the value AUTHENTICATED is returned. If they fail to identify the user, NOT_AUTHENTICATED is returned.

When eDirectory is the user store and a password has either expired or is expiring, the return values PWD_EXPIRED and PWD_EXPIRING can be returned respectively. See lines 102 - 108.

Line 111 demonstrates how an attribute is used to set an error message that is displayed to the user by calling the method getUserErrorMsg().

2.4.5 PasswordClass Example Code

package com.novell.nidp.authentication.local;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Properties;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.higgins.sts.api.ISecurityInformation;
import org.eclipse.higgins.sts.api.IUsernameToken;

import com.novell.nidp.NIDPConstants;
import com.novell.nidp.NIDPException;
import com.novell.nidp.NIDPPrincipal;
import com.novell.nidp.NIDPSession;
import com.novell.nidp.NIDPSessionData;
import com.novell.nidp.authentication.AuthnConstants;
import com.novell.nidp.common.authority.PasswordExpiredException;
import com.novell.nidp.common.authority.PasswordExpiringException;
import com.novell.nidp.common.authority.UserAuthority;
import com.novell.nidp.common.protocol.AuthnRequest;
import com.novell.nidp.liberty.wsc.cache.WSCCacheEntry;
import com.novell.nidp.logging.NIDPLog;
import com.novell.nidp.saml.SAMLAuthMethods;
import com.novell.security.sso.SecretStore;
import com.sun.xml.wss.impl.callback.UsernameCallback;

public class PasswordClass extends LocalAuthenticationClass implements STSAuthenticationClass, CallbackAuthentication {
    private String m_Error;

    // for NRL
    LocalAuthenticationClass basicClass = null;

    /**
     * Constructor for form based authentication
     * 
     * @param props
     *            Properties associated with the implementing class
     * @param uStores
     *            List of ordered user stores to authenticate against
     */
    public PasswordClass(Properties props, ArrayList<UserAuthority> uStores) {
        super(props, uStores);
        // for NRL
        if (m_LECP)
            basicClass = new BasicClass(props, uStores);
    }

    /**
     * Get the authentication type this class implements
     * 
     * @return returns the authentication type represented by this class
     */
    public String getType() {
        return AuthnConstants.PASSWORD;
    }

    public void initializeRequest(HttpServletRequest request, HttpServletResponse response, NIDPSession session, NIDPSessionData data, boolean following, String url) {
        super.initializeRequest(request, response, session, data, following, url);
        if (basicClass != null)
            basicClass.initializeRequest(request, response, session, data, following, url);
    }

    /**
     * Perform form based authentication. This method gets called on each
     * response during authentication process
     * 
     * @return returns the status of the authentication process which is one of
     *         AUTHENTICATED, NOT_AUTHENTICATED, CANCELLED, HANDLED_REQUEST,
     *         PWD_EXPIRING, PWD_EXPIRED
     */
    protected int doAuthenticate() {
        // If this is the first time the class is called following another
        // method
        // we want to display the form that will get the credentials. This
        // method
        // prevents a previous form from providing data to the next form if any
        // parameter names end up being the same
        if (!isFirstCallAfterPrevMethod()) {
            // This wasnt first time method was called, so see if data can be
            // processed
            int status = handlePostedData();
            if (status != NOT_AUTHENTICATED)
                return status;
        }

        String jsp = getProperty(AuthnConstants.PROPERTY_JSP);
        if (jsp == null || jsp.length() == 0)
            jsp = NIDPConstants.JSP_LOGIN;

        m_PageToShow = new PageToShow(jsp);
        m_PageToShow.addAttribute(NIDPConstants.ATTR_URL, (getReturnURL() != null ? getReturnURL() : m_Request.getRequestURL().toString()));
        if (getAuthnRequest() != null && getAuthnRequest().getTarget() != null)
            m_PageToShow.addAttribute("target", getAuthnRequest().getTarget());

        String username = m_Request.getParameter(NIDPConstants.PARM_USERID);
        if (username != null) // user name is already present
            m_PageToShow.addAttribute("username", username);

        // If we are displaying in the credential window and the error has not
        // been displayed yet, go ahead and show it. This can happened when
        // the wrong credentials as posted from a third party site
        String option = m_Request.getParameter("option");
        if (option != null && option.equals("credential") && m_Error != null) {
            m_PageToShow.addAttribute(NIDPConstants.ATTR_LOGIN_ERROR, m_Error);
            m_Error = null;
        }

        return SHOW_JSP;
    }

    protected int doAuthenticateNRL() {
        /*
         * Presently NRL always gets the credentials passed in the Basic header
         * over Liberty LECP So, invoking basic class todo the processing
         */
        int status = basicClass.doAuthenticate();
        if (basicClass.getPrincipal() != null) {
            this.setPrincipal(basicClass.getPrincipal());
            this.m_Credentials = basicClass.getCredentials();
        } else {
            this.m_ExpiredPrincipal = basicClass.getExpiredPrincipal();
            this.setErrorMsg(basicClass.getUserErrorMsg(), basicClass.getLogMsg());
            setFailure();
            this.m_PasswordException = basicClass.getPasswordException();
        }
        return status;
    }

    /**
     * Get and process the data that is posted from the form
     * 
     * @return returns the status of the authentication process which is one of
     *         AUTHENTICATED, NOT_AUTHENTICATED, CANCELLED, HANDLED_REQUEST,
     *         PWD_EXPIRING, PWD_EXPIRED
     */
    private int handlePostedData() {
        // Look for a name and password
        String id = m_Request.getParameter(NIDPConstants.PARM_USERID);
        String password = m_Request.getParameter(NIDPConstants.PARM_PASSWORD);

        setUserId(id);

        // Check to see if admin has setup for a custom query
        String ldapQuery = checkForQuery();

        try {
            // using admin defined attributes for query
            if (ldapQuery != null) {
                if (authenticateWithQuery(ldapQuery, password))
                    return AUTHENTICATED;
            }

            // If using default of name and password
            else {
                if (id == null || id.length() == 0)
                    return NOT_AUTHENTICATED;

                if (authenticateWithPassword(id, password))
                    return AUTHENTICATED;
            }
        } catch (PasswordExpiringException pe) {
            return PWD_EXPIRING;
        } catch (PasswordExpiredException pe) {
            return PWD_EXPIRED;
        }

        m_Error = getUserErrorMsg();
        return NOT_AUTHENTICATED;
    }

    public NIDPPrincipal handleSTSAuthentication(ISecurityInformation securityInformation) {
        IUsernameToken usernameToken = (IUsernameToken) securityInformation.getFirst(IUsernameToken.class);

        if (null != usernameToken) {
            try {
                if (authenticateWithPassword(usernameToken.getUsername(), usernameToken.getPassword()))
                    return getPrincipal();
            } catch (PasswordExpiringException pe) {
                return getPrincipal();
            } catch (PasswordExpiredException pe) {
            }
        }
        return null;
    }

    @Override
    public NIDPPrincipal cbAuthenticate(CallbackHandler cbHandler) {
        PasswordValidationCallback pwdCallback = new PasswordValidationCallback();
        Callback[] callbacks = new Callback[] { pwdCallback };

        NIDPPrincipal principal = null;
        try {
            cbHandler.handle(callbacks);
            if (pwdCallback.getUsername() != null) {
              
                String query = getProperty(AuthnConstants.PROPERTY_QUERY);
                String ldapQuery = null;
                boolean status = false;
                if (query != null)
                {
                  ldapQuery = getLDAPQueryString(query,pwdCallback.getUsername());
                	if (authenticateWithQuery(ldapQuery,pwdCallback.getPassword()))
                    status = true;
                }
                else if (authenticateWithPassword(pwdCallback.getUsername(), pwdCallback.getPassword()))
                status = true;
            	
              if ( status == true ) {
                    principal = getPrincipal();
                    principal.setAuthMethod(SAMLAuthMethods.PASSWORD);
                    return principal;
                }  
            }
                
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedCallbackException e) {
            if(NIDPLog.isLoggableWSTrustFine())
                NIDPLog.logWSTrustFine("The caller doesn't support password callback: " + e.getMessage());
        } catch (PasswordExpiredException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (PasswordExpiringException e) {
            principal = getPrincipal();
            principal.setAuthMethod(SAMLAuthMethods.PASSWORD);
            return principal;
        }
        return null;
    }
}