Developing a custom JASPIC authentication provider for Liberty

You can develop a custom Java™ Authentication SPI for Containers (JASPIC) authentication provider by creating classes that implement the required interfaces noted in the JSR 196: Java Authentication Service Provider Interface for Containers specification.

Before you begin

Review the specific interface implementation requirements for JASPIC authentication providers and modules in the JSR 196: Java Authentication Service Provider Interface for Containers specification.

About this task

WebSphere® Application Server Liberty supports the use of third-party authentication providers that are compliant with the servlet container specified in Java Authentication SPI for Containers (JASPIC) Version 1.1.

The servlet container defines interfaces that are used by the security runtime environment in collaboration with the web container in the WebSphere Application Server to invoke authentication modules before and after a web request is processed by an application. Authentication that uses JASPIC modules is performed only when JASPIC is enabled in the security configuration.

To develop a custom authentication provider, create classes that implement the required interfaces noted in the JSR 196: Java Authentication Service Provider Interface for Containers specification. A provider can use one or more authentication modules for authentication. Modules can use callbacks to perform authentication, or they can manually add the necessary user identity information to the client subject.

Procedure

  1. Create a class that implements thejavax.security.auth.message.config.AuthConfigProvider interface.
    The AuthConfigProvider implementation class must define a public two-argument constructor and the getServerAuthConfig public method:
    import java.util.Map;
    import javax.security.auth.callback.CallbackHandler;
    import javax.security.auth.message.AuthException;
    import javax.security.auth.message.config.AuthConfigFactory;
    import javax.security.auth.message.config.AuthConfigProvider;
    import javax.security.auth.message.config.ServerAuthConfig;
    
    public class SampleAuthConfigProvider implements AuthConfigProvider {
    
            public SampleAuthConfigProvider(Map<String, String> properties, AuthConfigFactory factory) {
                    ...
            }
            public ServerAuthConfig getServerAuthConfig(String layer, String appContext, CallbackHandler handler)
                    throws AuthException {
                    ...
            }
    }

    An instance of the AuthConfigProvider implementation class is used by WebSphere Application Server when a request arrives to be processed by the web module of the application. The getServerAuthConfig method is used to obtain a ServerAuthConfig instance. The CallbackHandler argument in the method call is used by the authentication module.

  2. Create a class that implements thejavax.security.auth.message.config.ServerAuthConfig interface.
    The ServerAuthConfig implementation class must define the getAuthContextID and getAuthContext public methods:
    import java.util.Map;
    import javax.security.auth.Subject;
    import javax.security.auth.message.AuthException;
    import javax.security.auth.message.MessageInfo;
    import javax.security.auth.message.config.ServerAuthConfig;
    import javax.security.auth.message.config.ServerAuthContext;
    
    public class SampleServerAuthConfig implements ServerAuthConfig {
    
            public String getAuthContextID(MessageInfo messageInfo) throws IllegalArgumentException {
                    ...
            }
            public ServerAuthContext getAuthContext(String authContextID, Subject serviceSubject, Map properties) 
                    throws AuthException {
                    ...
            }
    }

    The getAuthContextID and getAuthContext methods in the ServerAuthConfig implementation class are used to obtain a ServerAuthContext instance.

  3. Create a class that implements the javax.security.auth.message.config.ServerAuthContext interface.
    The ServerAuthContext implementation class must define the validateRequest and secureResponse public methods:
    import javax.security.auth.Subject;
    import javax.security.auth.message.AuthException;
    import javax.security.auth.message.AuthStatus;
    import javax.security.auth.message.MessageInfo;
    import javax.security.auth.message.config.ServerAuthContext;
    
    public class SampleServerAuthContext implements ServerAuthContext {
    
            public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) 
                    throws AuthException {
                    ...
            }
            public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) 
                    throws AuthException {
                    ...
            }
    }

    The validateRequest method in the ServerAuthContext implementation class is used to invoke the module that authenticates the received web request message. If the authentication result is successful, the web container dispatches the received web request message that the target web module processes in the application. If the authentication result is not successful, the request is rejected with the appropriate response status.

  4. Create a class that implements the javax.security.auth.message.module.ServerAuthModule interface.
    The ServerAuthModule implementation class must define the initialize, validateRequest, and secureResponse public methods:
    import javax.security.auth.Subject;
    import javax.security.auth.callback.CallbackHandler;
    import javax.security.auth.message.AuthException;
    import javax.security.auth.message.AuthStatus;
    import javax.security.auth.message.MessageInfo;
    import javax.security.auth.message.MessagePolicy;
    import javax.security.auth.message.module.ServerAuthModule;
    
    public class SampleAuthModule implements ServerAuthModule {
    
            public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, Map options) 
                    throws AuthException {
                    ...
            }
    
            public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject)
                    throws AuthException {
                    ...
            }
    
            public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) 
                    throws AuthException {
                    ...
            }
            public void cleanSubject(MessageInfo messageInfo, Subject subject)
                    throws AuthException {
                    ...
            }
    }

    The initialize method in the ServerAuthModule implementation class is called by the ServerAuthContext implementation class to initialize the authentication module and to associate it with the ServerAuthContext instance.

    The validateRequest and secureResponse methods in this class are used to authenticate thejavax.servlet.http.HttpServletRequest and javax.servlet.http.HttpServletResponse contained in thejavax.security.auth.message.MessageInfo that is received. These methods can use the CallbackHandler instance that is received in the initialize method to interact with the WebSphere security run time to validate a user password, and the active user registry to retrieve a unique id and group membership for a user. The retrieved data is placed in a Hashtable in the set of private credentials in the client subject. The WebSphere Application Server implementation of the CallbackHandler supports the following three callbacks:
    • CallerPrincipalCallback
    • GroupPrincipalCallback
    • PasswordValidationCallback

    WebSphere Application Server expects the name values obtained with PasswordValidationCallback.getUsername() and CallerPrincipalCallback.getName() to be identical. If they are not, unpredictable results occur. The handle() method of the CallbackHandler processes each callback that is given in the argument array of the method sequentially. Therefore, the name value set in the private credentials of the client subject is the one obtained from the last callback processed.

    If CallbackHandler is not used by the authentication module, and validateRequest returns a successful status, WebSphere Application Server requires that a Hashtable instance be included in the clientSubject with user identity information so that a custom login can be performed to obtain the credentials for the user. This Hashtable can be added to the client subject as in the following example:
    import java.util.Hashtable;
    import java.util.String;
    import javax.security.auth.Subject;
    import javax.security.auth.message.AuthException;
    import javax.security.auth.message.AuthStatus;
    import javax.security.auth.message.MessageInfo;
    import com.ibm.wsspi.security.registry.RegistryHelper;
    import com.ibm.wsspi.security.token.AttributeNameConstants.AttributeNameConstants;
    
    public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject)
           throws AuthException {
           ...
           UserRegistry reg = RegistryHelper.getUserRegistry(null);
           String uniqueid = reg.getUniqueUserID(username);
    
           Hashtable hashtable = new Hashtable();
           hashtable.put(AttributeNameConstants.WSCREDENTIAL_UNIQUEID, uniqueid);
           hashtable.put(AttributeNameConstants.WSCREDENTIAL_SECURITYNAME, username);
           hashtable.put(AttributeNameConstants.WSCREDENTIAL_PASSWORD, password);
           hashtable.put(AttributeNameConstants.WSCREDENTIAL_GROUPS, groupList); //optional
           clientSubject.getPrivateCredentials().add(hashtable);
           ...
    }

    For more information about the Hashtable requirements and custom login, see Developing JAAS custom login modules for a system login configuration.