Developing JAAS custom login modules for a system login configuration

For a Liberty server, multiple Java™ Authentication and Authorization Service (JAAS) plug-in points exist for configuring system logins. Liberty uses system login configurations to authenticate incoming requests. You can develop a custom JAAS login module to add information to the Subject of a system login configuration.

About this task

Application login configurations are called by servlet applications for obtaining a Subject that is based on specific authentication information. When you write a login module that plugs into a Liberty application login or system login configuration, you must develop login configuration logic that knows when specific information is present, and how to use the information. See JAAS configuration and JAAS login modules for more details.

To develop a JAAS custom login module for a system login configuration, follow the steps in the procedure:

Procedure

  • Understand usable callbacks and how they work.

    See Programmatic login for JAAS for more information about usable callbacks.

    Note: Liberty only supports the following callbacks:
    callbacks[0] = new javax.security.auth.callback.NameCallback("Username: ");
    callbacks[1] = new javax.security.auth.callback.PasswordCallback("Password: ", false);
    callbacks[2] = new com.ibm.websphere.security.auth.callback.WSCredTokenCallbackImpl("Credential Token: ");
    callbacks[3] = new com.ibm.websphere.security.auth.callback.WSServletRequestCallback("HttpServletRequest: ")
    callbacks[4] = new com.ibm.websphere.security.auth.callback.WSServletResponseCallback("HttpServletResponse: ");
    callbacks[5] = new com.ibm.websphere.security.auth.callback.WSAppContextCallback("ApplicationContextCallback: ");
    callbacks[6] = new WSRealmNameCallbackImpl("Realm Name: ", default_realm);
    callbacks[7] = new WSX509CertificateChainCallback("X509Certificate[]: ");
    callbacks[8] = wsAuthMechOidCallback = new WSAuthMechOidCallbackImpl("AuthMechOid: ");
  • Understand shared state variables and how they work.
    If you want to access the objects that the WebSphere® Application Server WebSphere Application Server traditional creates during a login, refer to the following shared state variables. For more information about these variables, see the System Programming Interfaces subtopic of Programming Interfaces.
    com.ibm.wsspi.security.auth.callback.Constants.WSPRINCIPAL_KEY
    Specifies an implemented object of the java.security.Principal interface. This shared state variable is for read-only purposes. Do not set this variable in the shared state for custom login modules. The default login module sets this variable.
    com.ibm.wsspi.security.auth.callback.Constants.WSCREDENTIAL_KEY
    Specifies the com.ibm.websphere.security.cred.WSCredential object. This shared state variable is for read-only purposes. Do not set this variable in the shared state for custom login modules. The default login module will set this variable.
    com.ibm.wsspi.security.auth.callback.Constants.WSSSOTOKEN_KEY
    Specifies the com.ibm.wsspi.security.token.SingleSignonToken object. Do not set this variable in the shared state for custom login modules. The default login module sets this variable.
  • Optional: Understand hashtables for custom JAAS login modules in Liberty. See Hash table login module for more details.
  • Develop a sample custom login module using callbacks and shared state.

    You can use the following sample to learn on how to use some of the callbacks and shared state variables.

    public class CustomCallbackLoginModule implements LoginModule {
    
      protected Map<String, ?> _sharedState;
      protected Subject _subject = null;
      protected CallbackHandler _callbackHandler;
      private final String customPrivateCredential = "CustomLoginModuleCredential";
    
      /**
       * Initialization of login module
       */
      public void initialize(Subject subject, CallbackHandler callbackHandler, 
                             Map<String, ?> sharedState, Map<String, ?> options) {
        _sharedState = sharedState;
        _subject = subject;
        _callbackHandler = callbackHandler;
      }
    
      public boolean login() throws LoginException {
        try {
          AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
            public Object run() throws Exception {
              _subject.getPrivateCredentials().add(customPrivateCredential);
              return null;
            }
    
          });
        } catch (PrivilegedActionException e) {
          throw new LoginException(e.getLocalizedMessage());
        }
    
        String username = null;
        char passwordChar[] = null;
        byte[] credToken = null;
        HttpServletRequest request = null;
        HttpServletResponse response = null;
        Map appContext = null;
        String realm = null;
        String authMechOid = null;
        java.security.cert.X509Certificate[] certChain = null;
    
        NameCallback nameCallback = null;
        PasswordCallback passwordCallback = null;
        WSCredTokenCallbackImpl wsCredTokenCallback = null;
        WSServletRequestCallback wsServletRequestCallback = null;
        WSServletResponseCallback wsServletResponseCallback = null;
        WSAppContextCallback wsAppContextCallback = null;
        WSRealmNameCallbackImpl wsRealmNameCallback = null;
        WSX509CertificateChainCallback wsX509CertificateCallback = null;
        WSAuthMechOidCallbackImpl wsAuthMechOidCallback = null;
    
        Callback[] callbacks = new Callback[9];
        callbacks[0] = nameCallback = new NameCallback("Username: ");
        callbacks[1] = passwordCallback = new PasswordCallback("Password: ", false);
        callbacks[2] = wsCredTokenCallback = new WSCredTokenCallbackImpl("Credential Token: ");
        callbacks[3] = wsServletRequestCallback = new WSServletRequestCallback("HttpServletRequest: ");
        callbacks[4] = wsServletResponseCallback = new WSServletResponseCallback("HttpServletResponse: ");
        callbacks[5] = wsAppContextCallback = new WSAppContextCallback("ApplicationContextCallback: ");
        callbacks[6] = wsRealmNameCallback = new WSRealmNameCallbackImpl("Realm name:");
        callbacks[7] = wsX509CertificateCallback = new WSX509CertificateChainCallback("X509Certificate[]: ");
        callbacks[8] = wsAuthMechOidCallback = new WSAuthMechOidCallbackImpl("AuthMechOid: ");
    
        try {
          _callbackHandler.handle(callbacks);
        } catch (Exception e) {
          // handle exception
        }
    
        if (nameCallback != null)
          username = nameCallback.getName();
    
        if (passwordCallback != null)
          passwordChar = passwordCallback.getPassword();
    
        if (wsCredTokenCallback != null)
          credToken = wsCredTokenCallback.getCredToken();
    
        if (wsServletRequestCallback != null)
          request = wsServletRequestCallback.getHttpServletRequest();
    
        if (wsServletResponseCallback != null)
          response = wsServletResponseCallback.getHttpServletResponse();
    
        if (wsAppContextCallback != null)
          appContext = wsAppContextCallback.getContext();
    
        if (wsRealmNameCallback != null)
          realm = wsRealmNameCallback.getRealmName();
    
        if (wsX509CertificateCallback != null)
          certChain = wsX509CertificateCallback.getX509CertificateChain();
    
        if (wsAuthMechOidCallback != null)
          authMechOid = wsAuthMechOidCallback.getAuthMechOid();
    
        _subject.getPrivateCredentials().add("username = " + username);
        _subject.getPrivateCredentials().add("password = " + String.valueOf(passwordChar));
        _subject.getPrivateCredentials().add("realm = " + realm);
        _subject.getPrivateCredentials().add("authMechOid = " + authMechOid.toString());
    
        return true;
      }
    
      public boolean commit() throws LoginException {
        return true;
      }
    
      public boolean abort() {
        return true;
      }
    
      public boolean logout() {
        return true;
      }
    
    }
  • Optional: Develop a sample custom login module using hashtable login.

    You can use the following sample to learn on how to use hashtable login.

    package com.ibm.websphere.security.sample;
    
    import java.util.Map;
    
    import javax.security.auth.Subject;
    import javax.security.auth.callback.CallbackHandler;
    import javax.security.auth.login.LoginException;
    import javax.security.auth.spi.LoginModule;
    
    import com.ibm.wsspi.security.token.AttributeNameConstants;
    
    /**
     * Custom login module that adds another PublicCredential to the subject
     */
    @SuppressWarnings("unchecked")
    public class CustomHashtableLoginModule implements LoginModule {
    
      protected Map<String, ?> _sharedState;
      protected Map<String, ?> _options;
    
    /**
     * Initialization of login module
     */
      public void initialize(
        Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
          _sharedState = sharedState;
          _options = options;
      }
    
      public boolean login() throws LoginException {
        try {
          java.util.Hashtable<String, Object> customProperties = (java.util.Hashtable<String, Object>) 
            _sharedState.get(AttributeNameConstants.WSCREDENTIAL_PROPERTIES_KEY);
          if (customProperties == null) {
            customProperties = new java.util.Hashtable<String, Object>();
          }
    
          customProperties.put(AttributeNameConstants.WSCREDENTIAL_USERID, "userId");
          // Sample of creating custom cache key
          customProperties.put(AttributeNameConstants.WSCREDENTIAL_CACHE_KEY, "customCacheKey");
    
          /*
           * Sample for creating user ID and security name
           * customProperties.put(AttributeNameConstants.WSCREDENTIAL_UNIQUEID, "userId");
           * customProperties.put(AttributeNameConstants.WSCREDENTIAL_SECURITYNAME, "securityName");
           * customProperties.put(AttributeNameConstants.WSCREDENTIAL_REALM, "realm");
           * customProperties.put(AttributeNameConstants.WSCREDENTIAL_GROUPS, "groupList");
           */
          /*
           * Sample for creating user ID and password
           * customProperties.put(AttributeNameConstants.WSCREDENTIAL_USERID, "userId");
           * customProperties.put(AttributeNameConstants.WSCREDENTIAL_PASSWORD, "password");
           */
          Map<String, java.util.Hashtable> mySharedState = (Map<String, java.util.Hashtable>) _sharedState;
          mySharedState.put(AttributeNameConstants.WSCREDENTIAL_PROPERTIES_KEY, customProperties);
        } catch (Exception e) {
            throw new LoginException("LoginException: " + e.getMessage());
        }
    
        return true;
      }
    
      public boolean commit() throws LoginException {
        return true;
      }
    
      public boolean abort() {
        return true;
      }
    
      public boolean logout() {
        return true;
      }
    }

What to do next

Add your custom login module into the WEB_INBOUND, and DEFAULT Java Authentication and Authorization Service (JAAS) system login configurations of the server.xml file. Put the custom login module class in a JAR file, for example, customLoginModule.jar, then make the JAR file available to the Liberty server. See Configuring a JAAS custom login module for Liberty.