WAS v8.5 > Develop applications > Develop security > Develop extensions to the WebSphere security infrastructure > Implement a custom authentication provider using JASPI

Develop a custom JASPI authentication provider

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

Review the specific interface implementation requirements for JASPI authentication providers and modules in the JSR 196: Java Authentication Service Provider Interface for Containers specification. WebSphere Application Server supports the use of third-party authentication providers that are compliant with the servlet container profile specified in Java Authentication SPI for Containers (JASPI) v1.0.

The servlet container profile defines interfaces used by the security runtime environment in collaboration with the web container in WAS to invoke authentication modules before and after a web request is processed by an application. Authentication using JASPI modules is performed only when JASPI has been enabled in the security configuration and when a configured JASPI provider has been associated with the web module that processes the received web request.

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. Depending on the scope of the provider, the implementation classes can be stored in various locations on the application server.

  1. Create a class that implements the javax.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 WAS 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(s).

  2. Create a class that implements the javax.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 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 {
                    ...
            }}

    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 respectively to authenticate the javax.servlet.http.HttpServletRequest and javax.servlet.http.HttpServletResponse contained in the javax.security.auth.message.MessageInfo that is received. These methods can use the CallbackHandler instance received in the initialize method to interact with the WebSphere security runtime to validate a user password, and the active user registry to retrieve a unique-id and membership groups for a user. The retrieved data is placed in a Hashtable in the set of private credentials in the client subject. The WAS implementation of CallbackHandler supports three callbacks:

    • CallerPrincipalCallback
    • GroupPrincipalCallback
    • PasswordValidationCallback

    WAS expects the name values obtained with PasswordValidationCallback.getUsername() and CallerPrincipalCallback.getName() to be identical. If they are not, unpredictable results occur. The CallbackHandler's handle() method processes each callback 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.

    Best practice: Always use PasswordValidationCallback to validate a user password and to add the appropriate credentials to the client subject during authentication:

    import javax.security.auth.Subject;
    import javax.security.auth.callback.Callback;
    import javax.security.auth.message.AuthException;
    import javax.security.auth.message.AuthStatus;
    import javax.security.auth.message.MessageInfo;
    import javax.security.auth.message.callback.PasswordValidationCallback;
    
    public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject)
           throws AuthException {
           ...
           PasswordValidationCallback pvcb = new PasswordValidationCallback(clientSubject, username, password);
           handler.handle(new Callback[] {pvcb});
           ...}
    bprac

    If CallbackHandler is not used by the authentication module, and validateRequest returns a successful status, WAS 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, read about Developing custom login modules for a system login configuration for JAAS.

    To support the login and authenticate methods of the Java Servlet 3.0 specification, the following logic must be added to the validateRequest method in the ServerAuthModule implementation class:

    import java.util.Map;
    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.servlet.http.HttpServletRequest;
    
    public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject)
           throws AuthException {
           ...
           Map msgMap = messageInfo.getMap();
    
           if ("login".equalsIgnoreCase(msgMap.get("com.ibm.websphere.jaspi.request"))) {
                  // This request is for the login method               String username = msgMap.get("com.ibm.websphere.jaspi.user");
                  String password = msgMap.get("com.ibm.websphere.jaspi.password");
                  // Authenticate using the user name and password set above.
           } 
           else if ("authenticate".equalsIgnoreCase(msgMap.get("com.ibm.websphere.jaspi.request"))) {
                  // this request is for the authenticate method               String authHeader
                       = ((HttpServletRequest) messageInfo.getRequestMessage()).getHeader("Authorization");
                  if (authHeader == null) {
                       // The user has not provided a username and password yet, return                    // AuthStatus.SEND_CONTINUE to challenge
                  } 
                  else {
                       // Authenticate using the user name and password in the authentication header.
                  }
           }
           else {
                  // This is not a Servlet 3.0 login or authenticate request; handle as usual.
           }
           ...}
  5. Compile all newly created classes.

    The following JAR files in your WAS installation must be specified in the class path to successfully compile the new classes:

  6. Create a JAR file with the compiled classes.

    Depending on the requirements, the JAR file can be placed in one of three locations:

    • app_server_root/lib

      This location is always on the classpath for the WAS classloader. Using this location, the provider can be registered for a set of web modules or applications as the cell or domain default provider for all web modules and applications, and it can be registered manually as a persistent provider.

    • Shared library

      Place the provider JAR file anywhere on the WAS system. Configure a shared library that points to the JAR, and add that shared library to the application or server classpath. In a shared library, the provider can be registered for a set of web modules or applications, but the provider cannot be used as the cell or domain default provider. It also cannot be registered as a persistent provider because the shared library is not in the classpath for provider registration during server startup. For more information about configuring a shared library, read about Creating shared libraries.

    • Embedded in the application

      Include the provider JAR file in the application's EAR file as a utility JAR, or embed the compiled class files in the web module WAR. The embedded provider can be registered for the web modules in the application as long as the classes are included in the classpath for the web module. This provider cannot be used as a cell or domain default provider, nor can it be registered as a persistent provider. The classes in the application are not available for provider registration during server startup.

  7. Configure the provider in the security configuration using the dmgr console or an administration script.

    Read about Configure a new JASPI authentication provider or JaspiManagement command group for AdminTask for more information.


Related


Develop custom login modules for a system login configuration for JAAS
Create shared libraries
Implement a custom authentication provider using JASPI
Configure a new JASPI authentication provider
Modify an existing JASPI authentication provider
Delete a JASPI authentication provider
Enable JASPI authentication using the Map JASPI provider option during application deployment


Reference:

JaspiManagement command group for AdminTask
JASPI authentication providers page
JASPI authentication provider details


Related information:

JSR 196: Java Authentication Service Provider Interface for Containers

IBM WebSphere Developer Technical Journal: Advanced authentication in WAS


+

Search Tips   |   Advanced Search