2.4 Understanding the Authentication Class Example

This section demonstrates how a password authentication class might be implemented 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 line 12 of PasswordClass Example Code. The LocalAuthenticationClass has a single constructor that must be called as shown in lines 29 - 32. 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 76 - 116 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 84 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 88). 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 93 - 115 are encountered in the PasswordClass Example Code, it has been determined that a page needs to be returned via 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 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 147 - 191 in the PasswordClass Example Code). Lines 151 - 153 check to see if canceling the authentication should occur. Lines 156 - 157 attempt to obtain the credentials.

Line 160 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 166. If a query is not available, the authenticateWithPassword() method is called at line 176.

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 180-186.

Line 189 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

 1 package com.novell.nidp.authentication.local;
 2 
 3 import java.util.*;
 4 
 5 import com.novell.nidp.*;
 6 import com.novell.nidp.authentication.*;
 7 import com.novell.nidp.common.authority.*;
 8
 9  /** An example of the implementation of LocalAuthenticationClass.
 10  */
 11
 12 public class PasswordClass extends LocalAuthenticationClass
 13 {
 14    /**
 15     * ***
 16     * 
 17     */
 18
 19    private static final String CANCEL_X = "Cancel.x";
 20    /**
 21     * Constructs the form-based authentication methods.
 22     *
 23     * @param props      Properties associated with the implementing
 24                         class.
 25     * @param uStores    List of ordered user stores to authenticate
 26     *                   against.
 27     */
 28
 29    public PasswordClass(Properties props, ArrayList uStores)
 30    {
 31        super(props,uStores);
 32    }
 33
 34   /**
 35     * Gets the authentication type this class implements.
 36     * 
 37     * @return The authentication type represented by this class.
 38     */
 39    public String getType()
 40    {
 41        return AuthnConstants.PASSWORD;
 42    }
 43 
 44    /** Performs form-based authentication. This method gets called 
 45     *  on each response
 46     *  during the authentication process.
 47     *
 48     * @return Returns the status of the authentication process:
 49     *
 50     *  * AUTHENTICATED: Specifies that the authentication
 51     *    succeeded in identifying a single NIDPPrincipal object
 52     *    (user).
 53     *  * NOT_AUTHENTICATED: Specifies that the authentication 
 54     *    failed.
 55     *  * CANCELLED: Specifies that the authentication process was
 56     *    canceled. This typically occurs only during authentication
 57     *    after a request from a service provider.
 58     *  * HANDLED_REQUEST: The request has been handled and 
 59     *    a response
 60     *    provided. Further processing or information is needed to
 61     *    complete authentication. Typically, this value is returned
 62     *    when a page is returned that will query for credentials.
 63     *  * PWD_EXPIRING: Successful authentication but the user's 
 64     *    password is about to expire. This condition results in a
 65     *    redirection to the expired password servlet (if it is 66
 66     *    defined on the authentication contract).
 67     *  * PWD_EXPIRED: Successful authentication but the user's
 68     *    password is expired. This condition results in a
 69     *    redirection to expired password servlet (if it is defined
 70     *    on the authentication contract).
 71     *
 72     * @throws NIDPException  Specifies how the authentication
 73     *                        process proceeds***.
 74     */
 75   
 76    protected int doAuthenticate()
 77    throws NIDPException
 78    {
 79        // If this is the first time the class is called following
 80        // another method, we want to display the form that will get
 81        // the credentials. This method prevents a previous form from
 82        // providing data to the next form if any parameter names end
 83        // up being the same.
 84        if (!isFirstCallAfterPrevMethod())
 85        {
 86            // This wasn't first time the method was called, so see if
 87            // data can be processed.
 88            int status = handlePostedData();
 89            if (status != NOT_AUTHENTICATED)
 90                return status;
 91        }
 92        
 93        // If this is an active login, display login page
 94        if (getProvisionURL() != null)
 95
 96          m_Request.setAttribute(NIDPConstants.RESP_ATTR_PROVISION,
               getProvisionURL());
 97
 98        if (isCancelAppropriate())
 99           m_Request.setAttribute(NIDPConstants.RESP_ATTR_CANCEL,
                NIDPConstants.VALUE_TRUE);
 100
 101        if (isUserIdentification())
 102          m_Request.setAttribute(NIDPConstants.RESP_ATTR_IDENTIFY,
                NIDPConstants.VALUE_TRUE);
 103        
 104        // If we are just identifying the user, we do not show
 105           federatable providers.
 106        else
 107           m_Request.setAttribute(NIDPConstants.RESP_ATTR_PROVIDERS,
                getIDPProvide rs());
 108        
 109        String jsp = getProperty(AuthnConstants.PROPERTY_JSP); 
 110        if (jsp == null || jsp.length() == 0)
 111            jsp = NIDPConstants.JSP_LOGIN;
 112
 113        m_Request.setAttribute(NIDPConstants.ATTR_URL,
              (getReturnURL() != null ? getReturnURL() :
            m_Request.getRequestURL().toString()));
 114        showJSP(jsp);
 115        return HANDLED_REQUEST;
 116    }
 117     
 118    /**
 119     * Get and process the data that is posted from the form.
 120     *
 121     * @return  Returns the status of the authentication process 
 122     * which is one of the following values:
 123     *      
 124     *  * AUTHENTICATED: Specifies that the authentication
 125     *    succeeded in identifying a single NIDPPrincipal object
 126     *    (user).
 127     *  * NOT_AUTHENTICATED: Specifies that the authentication
 128     *    failed.
 129     *  * CANCELLED: Specifies that the authentication process was
 130     *    canceled. This typically occurs only during authentication
 131     *    after a request from a service provider.
 132     *  * HANDLED_REQUEST: The request has been handled and
 133     *    a response provided. Further processing or information is
 134     *    is needed to complete authentication. Typically, this
 135     *    value is returned when a page is returned that will
 136     *    query for credentials.
 137     *  * PWD_EXPIRING: Successful authentication but the
 138     *    user's password is about to expire. This condition results
 139     *    in a redirection to the expired password servlet (if it
 140     *    is defined on the authentication contract).
 141     *  * PWD_EXPIRED: Successful authentication but the
 142     *    user's password is expired. This condition results in a
 143     *    redirection to expired password servlet (if it is defined
 144     *    on the authentication contract).
 145     */      
 146
 147    private int handlePostedData()
 148    {
 149        String cancel = m_Request.getParameter(CANCEL_X);
 150        
 151        // If we got a cancel and we are to check for a cancel.
 152        if (cancel != null && isCancelAppropriate())
 153            return CANCEL;
 154                    
 155        // Look for a name and password.
 156        String id = m_Request.getParameter
              (NIDPConstants.PARM_USERID);
 157        String password = m_Request.getParameter
              (NIDPConstants.PARM_PASSWORD);
 158        
 159        // Check to see if admin has setup for a custom query.
 160        String query = getProperty(AuthnConstants.PROPERTY_QUERY);
 161        boolean hasQuery = (query != null && query.length() > 0);
 162
 163        try
 164        {
 165            // Using admin defined attributes for query.
 166            if (hasQuery && authenticateWithQuery
                   (getLDAPQueryString(query),password))
 167                return AUTHENTICATED;
 169
 170            // If using default of name and password.
 171            else if (!hasQuery)
 172            {
 173                if (id == null || id.length() == 0)
 174                    return NOT_AUTHENTICATED;
 175
 176                if (authenticateWithPassword(id,password))
 177                    return AUTHENTICATED;
 178            }            
 179        }
 180        catch (PasswordExpiringException pe)
 181        {
 182            return PWD_EXPIRING;
 183        }
 184        catch (PasswordExpiredException pe)
 185        {
 186            return PWD_EXPIRED;
 187        }
 188
 189        m_Request.setAttribute
            (NIDPConstants.ATTR_LOGIN_ERROR, getUserErrorMsg());
 190        return NOT_AUTHENTICATED;
 191    }
 192
 193    /** Gets a representative LDAP query to find a user 
 194     *  in a directory***.
 195     *
 196     * @return Returns an LDAP query form string that can be used 
 197     *         to search a directory.
 198     * @param query  */
 199    private String getLDAPQueryString(String query)
 200    {
 201        StringBuffer buf = new StringBuffer();
 202        int idx  = 0;
 203        int idx2 = 0;
 204        do
 205        {
 206            idx2 = query.indexOf("%",idx);
 207            if (idx2 != -1)
 208            {
 209                buf.append(query.substring(idx,idx2));
 210                int idx3 = query.indexOf("%",idx2+1);
 211                if (idx3 != -1)
 212                {
 213                    String param = query.substring(idx2+1,idx3);
 214                    String value = m_Request.getParameter(param);
 215                    if (value != null && value.length() > 0)
 216                        buf.append(value);
 217
 218                    idx = idx3 + 1;
 219                }
 220            }
 221            else
 222                buf.append(query.substring(idx));
 223
 224        } while(idx2 != -1);
 225
 226        return buf.toString();
 227    }
 228 }