4 Authentication Providers

This chapter describes authentication provider concepts and functionality, and provides step-by-step instructions for developing a custom authentication provider.

Authentication is the mechanism by which callers prove that they are acting on behalf of specific users or systems. Authentication answers the question, "Who are you?" using credentials such as username/password combinations.

In WebLogic Server, authentication providers are used to prove the identity of users or system processes. Authentication providers also remember, transport, and make that identity information available to various components of a system (via subjects) when needed. During the authentication process, a principal validation provider provides additional security protections for the principals (users and groups) contained within the subject by signing and verifying the authenticity of those principals. (See Principal Validation Providers.)

This chapter includes the following sections:

Authentication Concepts

Before delving into the specifics of developing custom authentication providers, it is important to understand the following concepts:

Users and Groups, Principals and Subjects

A user is similar to an operating system user in that it represents a person. A group is a category of users, classified by common traits such as job title. Categorizing users into groups makes it easier to control the access permissions for large numbers of users. See Users, Groups, and Security Roles in Securing Resources Using Roles and Policies for Oracle WebLogic Server.

Both users and groups can be used as principals by application servers like WebLogic Server. A principal is an identity assigned to a user or group as a result of authentication. The Java Authentication and Authorization Service (JAAS) requires that subjects be used as containers for authentication information, including principals. Each principal stored in the same subject represents a separate aspect of the same user's identity, much like cards in a person's wallet. (For example, an ATM card identifies someone to their bank, while a membership card identifies them to a professional organization to which they belong.) See Java Authentication and Authorization Service (JAAS).

Note:

Subjects replace WebLogic Server 6.x users.

Figure 4-1 illustrates the relationships among users, groups, principals, and subjects.

Figure 4-1 Relationships Among Users, Groups, Principals and Subjects

Description of Figure 4-1 follows
Description of "Figure 4-1 Relationships Among Users, Groups, Principals and Subjects"

As part of a successful authentication, principals are signed and stored in a subject for future use. A principal validation provider signs principals, and an authentication provider's LoginModule actually stores the principals in the subject. Later, when a caller attempts to access a principal stored within a subject, a principal validation provider verifies that the principal has not been altered since it was signed, and the principal is returned to the caller (assuming all other security conditions are met).

Note:

See Principal Validation Providers and LoginModules, respectively.

Any principal that is going to represent a WebLogic Server user or group needs to implement the WLSUser and WLSGroup interfaces, which are available in the weblogic.security.spi package.

Providing Initial Users and Groups

Authentication providers need a list of users and groups before they can be used to perform authentication in a running WebLogic Server. Some authentication providers let the administrator configure an external database (for example, add the users and groups to an LDAP server or a DBMS) and then configure the provider to use that database. These providers don't have to worry about how the users and groups are populated because the administrator does that first, using the external database's tools.

However, some authentication providers create and manage their own list of users and groups. This is the case for the ManageableSampleAuthenticator provider. These providers need to worry about how their initial set of users and groups is populated. One way to handle this is for the provider's "initialize" method to notice that the users and groups don't exist yet, and then populate the list with an initial set of users and groups.

Note that some providers have a separate list of users and groups for each security realm, and therefore need to create an initial set of users and groups the first time the list is used in a new realm. For example, the ManageableSampleAuthenticator provider creates a separate properties file of users and groups for each realm. Its initialize method gets the realm name, determines whether the properties file for that realm exists and, if not, creates one, populating it with its initial set of users and groups.

LoginModules

A LoginModule is a required component of an authentication provider, and can be a component of an identity assertion provider if you want to develop a separate LoginModule for perimeter authentication.

LoginModules are the work-horses of authentication: all LoginModules are responsible for authenticating users within the security realm and for populating a subject with the necessary principals (users/groups). LoginModules that are not used for perimeter authentication also verify the proof material submitted (for example, a user's password).

If there are multiple authentication providers configured in a security realm, each of the authentication providers' LoginModules will store principals within the same subject. Therefore, if a principal that represents a WebLogic Server user (that is, an implementation of the WLSUser interface) named "Joe" is added to the subject by one authentication provider's LoginModule, any other authentication provider in the security realm should be referring to the same person when they encounter "Joe". In other words, the other authentication providers' LoginModules should not attempt to add another principal to the subject that represents a WebLogic Server user (for example, named "Joseph") to refer to the same person. However, it is acceptable for a another authentication provider's LoginModule to add a principal of a type other than WLSUser with the name "Joseph".

The LoginModule Interface

LoginModules can be written to handle a variety of authentication mechanisms, including username/password combinations, smart cards, biometric devices, and so on. You develop LoginModules by implementing the javax.security.auth.spi.LoginModule interface, which is based on the Java Authentication and Authorization Service (JAAS) and uses a subject as a container for authentication information. The LoginModule interface enables you to plug in different kinds of authentication technologies for use with a single application, and the WebLogic Security Framework is designed to support multiple LoginModule implementations for multipart authentication. You can also have dependencies across LoginModule instances or share credentials across those instances. However, the relationship between LoginModules and authentication providers is one-to-one. In other words, to have a LoginModule that handles retina scan authentication and a LoginModule that interfaces to a hardware device like a smart card, you must develop and configure two authentication providers, each of which include an implementation of the LoginModule interface. See Implement the JAAS LoginModule Interface.

Note:

You can also obtain LoginModules from third-party security vendors instead of developing your own.

LoginModules and Multipart Authentication

The way you configure multiple authentication providers (and thus, multiple LoginModules) can affect the overall outcome of the authentication process, which is especially important for multipart authentication. First, because LoginModules are components of authentication providers, they are called in the order in which the authentication providers are configured. Generally, you configure authentication providers using the WebLogic Server Administration Console. (See Specifying the Order of Authentication Providers.) Second, the way each LoginModule's control flag is set specifies how a failure during the authentication process should be handled. Figure 4-2 illustrates a sample flow involving three different LoginModules (that are part of three authentication providers), and illustrates what happens to the subject for different authentication outcomes.

Figure 4-2 Sample LoginModule Flow

Description of Figure 4-2 follows
Description of "Figure 4-2 Sample LoginModule Flow"

If the control flag for Custom Authentication Provider #1 had been set to Required, the authentication failure in its User Authentication step would have caused the entire authentication process to have failed. Also, if the user had not been authenticated by the WebLogic Authentication provider (or custom authentication provider #2), the entire authentication process would have failed. If the authentication process had failed in any of these ways, all three LoginModules would have been rolled back and the subject would not contain any principals.

Note:

See Java Authentication and Authorization Service (JAAS) LoginModule Developer's Guide (http://docs.oracle.com/javase/8/docs/technotes/guides/security/jaas/JAASLMDevGuide.html) and the LoginModule interface (http://docs.oracle.com/javase/8/docs/api/javax/security/auth/spi/LoginModule.html), respectively.

Java Authentication and Authorization Service (JAAS)

Whether the client is an application, applet, Enterprise JavaBean (EJB), or servlet that requires authentication, WebLogic Server uses the Java Authentication and Authorization Service (JAAS) classes to reliably and securely authenticate to the client. JAAS implements a Java version of the Pluggable Authentication Module (PAM) framework, which permits applications to remain independent from underlying authentication technologies. Therefore, the PAM framework allows the use of new or updated authentication technologies without requiring modifications to your application.

WebLogic Server uses JAAS for remote fat-client authentication, and internally for authentication. Therefore, only developers of custom authentication providers and developers of remote fat client applications need to be involved with JAAS directly. Users of thin clients or developers of within-container fat client applications (for example, those calling an Enterprise JavaBean (EJB) from a servlet) do not require the direct use or knowledge of JAAS.

How JAAS Works With the WebLogic Security Framework

Generically, authentication using the JAAS classes and WebLogic Security Framework is performed in the following manner:

  1. A client-side application obtains authentication information from a user or system process. The mechanism by which this occurs is different for each type of client.

  2. The client-side application can optionally create a CallbackHandler containing the authentication information.

    1. The client-side application passes the CallbackHandler to a local (client-side) LoginModule using the LoginContext class. (The local LoginModule could be UsernamePasswordLoginModule, which is provided as part of WebLogic Server.)

    2. The local LoginModule passes the CallbackHandler containing the authentication information to the appropriate WebLogic Server container (for example, RMI, EJB, servlet, or IIOP).

      Note:

      A CallbackHandler is a highly-flexible JAAS standard that allows a variable number of arguments to be passed as complex objects to a method. There are three types of CallbackHandlers: NameCallback, PasswordCallback, and TextInputCallback, all of which reside in the javax.security.auth.callback package. The NameCallback and PasswordCallback return the username and password, respectively. TextInputCallback can be used to access the data users enter into any additional fields on a login form (that is, fields other than those for obtaining the username and password). When used, there should be one TextInputCallback per additional form field, and the prompt string of each TextInputCallback must match the field name in the form. WebLogic Server only uses the TextInputCallback for form-based Web application login. See the CallbackHandler interface (http://docs.oracle.com/javase/8/docs/api/javax/security/auth/callback/CallbackHandler.html).

      See the LoginContext class (http://docs.oracle.com/javase/8/docs/api/javax/security/auth/login/LoginContext.html).

      See WebLogic Server API Reference Javadoc for the UsernamePasswordLoginModule class.

      If you do not want to use a client-side LoginModule, you can specify the username and password in other ways: for example, as part of the initial JNDI lookup.

  3. The WebLogic Server container calls into the WebLogic Security Framework. If there is a client-side CallbackHandler containing authentication information, this is passed into the WebLogic Security Framework.

  4. For each of the configured authentication providers, the WebLogic Security Framework creates a CallbackHandler using the authentication information that was passed in. (These are internal CallbackHandlers created on the server-side by the WebLogic Security Framework, and are not related to the client's CallbackHandler.)

  5. The WebLogic Security Framework calls the LoginModule associated with the authentication provider (that is, the LoginModule that is specifically designed to handle the authentication information).

    Note:

    See LoginModules.

    The LoginModule attempts to authenticate the client using the authentication information.

  6. If the authentication is successful, the following occurs:

    1. Principals (users and groups) are signed by a principal validation provider to ensure their authenticity between programmatic server invocations. See Principal Validation Providers.

    2. The LoginModule associates the signed principals with a subject, which represents the user or system process being authenticated. See Users and Groups, Principals and Subjects.

      Note:

      For authentication performed entirely on the server-side, the process would begin at step 3, and the WebLogic Server container would call the weblogic.security.services.authentication.login method prior to step 4.

Example: Standalone T3 Application

Figure 4-3 illustrates how the JAAS classes work with the WebLogic Security Framework for a standalone, T3 application, and an explanation follows.

Figure 4-3 Authentication Using JAAS Classes and WebLogic Server

Description of Figure 4-3 follows
Description of "Figure 4-3 Authentication Using JAAS Classes and WebLogic Server"

For this example, authentication using the JAAS classes and WebLogic Security Framework is performed in the following manner:

  1. The T3 application obtains authentication information (username, password, and URL) from a user or system process.

  2. The T3 application creates a CallbackHandler containing the authentication information.

    1. The T3 application passes the CallbackHandler to the UsernamePasswordLoginModule using the LoginContext class.

      Note:

      The weblogic.security.auth.login.UsernamePasswordLoginModule implements the standard JAAS javax.security.auth.spi.LoginModule interface and uses client-side APIs to authenticate a WebLogic client to a WebLogic Server instance. It can be used for both T3 and IIOP clients. Callers of this LoginModule must implement a CallbackHandler to pass the username (NameCallback), password (PasswordCallback), and a URL (URLCallback).

    2. The UsernamePasswordLoginModule passes the CallbackHandler containing the authentication information (that is, username, password, and URL) to the WebLogic Server RMI container.

  3. The WebLogic Server RMI container calls into the WebLogic Security Framework. The client-side CallbackHandler containing authentication information is passed into the WebLogic Security Framework.

  4. For each of the configured authentication providers, the WebLogic Security Framework creates a CallbackHandler containing the username, password, and URL that was passed in. (These are internal CallbackHandlers created on the server-side by the WebLogic Security Framework, and are not related to the client's CallbackHandler.)

  5. The WebLogic Security Framework calls the LoginModule associated with the authentication provider (that is, the LoginModule that is specifically designed to handle the authentication information).

    The LoginModule attempts to authenticate the client using the authentication information.

  6. If the authentication is successful, the following occurs:

    1. Principals (users and groups) are signed by a principal validation provider to ensure their authenticity between programmatic server invocations.

    2. The LoginModule associates the signed principals with a subject, which represents the user or system being authenticated.

    3. The WebLogic Security Framework returns the authentication status to the T3 client application, and the T3 client application retrieves the authenticated subject from the WebLogic Security Framework.

The Authentication Process

Figure 4-4 shows a behind-the-scenes look of the authentication process for a fat-client login. JAAS runs on the server to perform the login. Even in the case of a thin-client login (that is, a browser client) JAAS is still run on the server.

Figure 4-4 The Authentication Process

Description of Figure 4-4 follows
Description of "Figure 4-4 The Authentication Process"

Note:

Only developers of custom authentication providers will be involved with this JAAS process directly. The client application could either use JNDI initial context creation or JAAS to initiate the passing of the username and password.

When a user attempts to log into a system using a username/password combination, WebLogic Server establishes trust by validating that user's username and password, and returns a subject that is populated with principals per JAAS requirements. As Figure 4-4 also shows, this process requires the use of a LoginModule and a principal validation provider, which are discussed in detail in LoginModules and Principal Validation Providers respectively.

After successfully proving a caller's identity, an authentication context is established, which allows an identified user or system to be authenticated to other entities. Authentication contexts may also be delegated to an application component, allowing that component to call another application component while impersonating the original caller.

Do You Need to Develop a Custom Authentication Provider?

The default (that is, active) security realm for WebLogic Server includes a WebLogic Authentication provider.

Note:

In conjunction with the WebLogic Authorization provider, the WebLogic Authentication provider replaces the functionality of the File realm that was available in 6.x releases of WebLogic Server.

The WebLogic Authentication provider supports delegated username/password authentication, and utilizes an embedded LDAP server to store user and group information. The WebLogic Authentication provider allows you to edit, list, and manage users and group membership.

WebLogic Server also provides the following additional authentication providers that you can use instead of or in conjunction with the WebLogic Authentication provider in the default security realm:

  • A set of LDAP authentication providers that access external LDAP stores (including Open LDAP, iPlanet, Microsoft Active Directory, and Novell NDS).

  • A set of Database Base Management System (DBMS) authentication providers that access user, password, group, and group membership information stored in databases for authentication

  • A Windows NT Authentication provider that uses Windows NT users and groups for authentication purposes.

  • An LDAP X509 Identity Assertion provider.

By default, these additional authentication providers are available but not configured in the WebLogic default security realm.

If you want to perform additional authentication tasks, then you need to develop a custom authentication provider.

Note:

If you want to perform perimeter authentication using a token type that is not supported out of the box (for example, a new, custom, or third party token type), you might need to develop a custom identity assertion provider. See Identity Assertion Providers.

How to Develop a Custom Authentication Provider

If the WebLogic Authentication provider does not meet your needs, you can develop a custom authentication provider by following these steps:

  1. Create Runtime Classes Using the Appropriate SSPIs

  2. Generate an MBean type for your custom authentication provider by completing the steps described in Generate an MBean Type Using the WebLogic MBeanMaker.

  3. Configure the Custom Authentication Provider Using the Administration Console

Create Runtime Classes Using the Appropriate SSPIs

Before you start creating runtime classes, you should first:

When you understand this information and have made your design decisions, create the runtime classes for your custom authentication provider by following these steps:

For an example of how to create a runtime class for a custom authentication provider, see Example: Creating the Runtime Classes for the Sample Authentication Provider.

Implement the AuthenticationProviderV2 SSPI

Note:

The AuthenticationProvider SSPI is deprecated in this release of WebLogic Server. Use the AuthenticationProviderV2 SSPI instead.

To implement the AuthenticationProviderV2 SSPI, provide implementations for the methods described in Understand the Purpose of the Provider SSPIs and the following methods:

  • getLoginModuleConfiguration

    public AppConfigurationEntry getLoginModuleConfiguration()
    

    The getLoginModuleConfiguration method obtains information about the authentication provider's associated LoginModule, which is returned as an AppConfigurationEntry. The AppConfigurationEntry is a Java Authentication and Authorization Service (JAAS) class that contains the classname of the LoginModule; the LoginModule's control flag (which was passed in via the authentication provider's associated MBean); and a configuration options map for the LoginModule (which allows other configuration information to be passed into the LoginModule).

    To know about the AppConfigurationEntry class (located in the javax.security.auth.login package) and the control flag options for LoginModules, see the AppConfigurationEntry class (http://docs.oracle.com/javase/8/docs/api/javax/security/auth/login/AppConfigurationEntry.html) and the Configuration class (http://docs.oracle.com/javase/8/docs/api/javax/security/auth/login/Configuration.html). See LoginModulesand Understand Why You Need an MBean Type.

  • getAssertionModuleConfiguration

    public AppConfigurationEntry
    getAssertionModuleConfiguration()
    

    The getAssertionModuleConfiguration method obtains information about an identity assertion provider's associated LoginModule, which is returned as an AppConfigurationEntry. The AppConfigurationEntry is a JAAS class that contains the classname of the LoginModule; the LoginModule's control flag (which was passed in via the identity assertion provider's associated MBean); and a configuration options map for the LoginModule (which allows other configuration information to be passed into the LoginModule).

    Note:

    The implementation of the getAssertionModuleConfiguration method can be to return null, if you want the identity assertion provider to use the same LoginModule as the authentication provider.

    The assertIdentity() method of an identity assertion provider is called every time identity assertion occurs, but the LoginModules may not be called if the Subject is cached. The -Dweblogic.security.identityAssertionTTL flag can be used to affect this behavior (for example, to modify the default TTL of 5 minutes or to disable the cache by setting the flag to -1).

    It is the responsibility of the identity assertion provider to ensure not just that the token is valid, but also that the user is still valid (for example, the user has not been deleted).

    To use the EJB <run-as-principal> element with a custom authentication provider, use the getAssertionModuleConfiguration() method. This method performs the identity assertion that validates the principal specified in the <run-as-principal>element.

  • getPrincipalValidator

    public PrincipalValidator getPrincipalValidator()
    

    The getPrincipalValidator method obtains a reference to the principal validation provider's runtime class (that is, the PrincipalValidator SSPI implementation). In most cases, the WebLogic Principal Validation provider can be used (see Example 4-1 for an example of how to return the WebLogic Principal Validation provider). See Principal Validation Providers.

  • getIdentityAsserter

    public IdentityAsserterV2 getIdentityAsserter()
    

    The AuthenticationProviderV2 getIdentityAsserter method obtains a reference to the new identity assertion provider's runtime class (that is, the IdentityAsserterV2 SSPI implementation).

    In most cases, the return value for this method will be null (see Example 4-1 for an example). See Identity Assertion Providers.

See Java API Reference for Oracle WebLogic Server to know more about the AuthenticationProviderV2 SSPI and the methods described above.

Implement the JAAS LoginModule Interface

To implement the JAAS javax.security.auth.spi.LoginModule interface, provide implementations for the following methods:

  • initialize

    public void initialize (Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)
    

    The initialize method initializes the LoginModule. It takes as arguments a subject in which to store the resulting principals, a CallbackHandler that the authentication provider will use to call back to the container for authentication information, a map of any shared state information, and a map of configuration options (that is, any additional information you want to pass to the LoginModule).

    A CallbackHandler is a highly-flexible JAAS standard that allows a variable number of arguments to be passed as complex objects to a method. See the Java SE 8.0 API Specification for the CallbackHandler interface (http://docs.oracle.com/javase/8/docs/api/javax/security/auth/callback/CallbackHandler.html).

  • login

    public boolean login() throws LoginException
    

    The login method attempts to authenticate the user and create principals for the user by calling back to the container for authentication information. If multiple LoginModules are configured (as part of multiple authentication providers), this method is called for each LoginModule in the order that they are configured. Information about whether the login was successful (that is, whether principals were created) is stored for each LoginModule.

  • commit

    public boolean commit() throws LoginException
    

    The commit method attempts to add the principals created in the login method to the subject. This method is also called for each configured LoginModule (as part of the configured authentication providers), and executed in order. Information about whether the commit was successful is stored for each LoginModule.

  • abort

    public boolean abort() throws LoginException
    

    The abort method is called for each configured LoginModule (as part of the configured authentication providers) if any commits for the LoginModules failed (in other words, the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules did not succeed). The abort method will remove that LoginModule's principals from the subject, effectively rolling back the actions performed. See the LoginModule interface (http://docs.oracle.com/javase/8/docs/api/javax/security/auth/spi/LoginModule.html).

  • logout

    public boolean logout() throws LoginException
    

    The logout method attempts to log the user out of the system. It also resets the subject so that its associated principals are no longer stored.

    Note:

    The LoginModule.logout method is never called for the WebLogic Authentication providers or custom authentication providers. This is simply because once the principals are created and placed into a subject, the WebLogic Security Framework no longer controls the lifecycle of the subject. Therefore, the developer-written, user code that creates the JAAS LoginContext to login and obtain the subject should also call the LoginContext.logout method. When the user code runs in a Java client that uses JAAS directly, that code has the option of calling the LoginContext.logout method, which clears the subject. When the user code runs in a servlet, the servlet has the ability to logout a user from a servlet session, which clears the subject.

See the Java Authentication and Authorization Service (JAAS) Developer's Guide (http://docs.oracle.com/javase/8/docs/technotes/guides/security/jaas/JAASLMDevGuide.html) and the LoginModule interface (http://docs.oracle.com/javase/8/docs/api/javax/security/auth/spi/LoginModule.html).

Throwing Custom Exceptions from LoginModules

You may want to throw a custom exception from a LoginModule you write. The custom exception can then be caught by your application and appropriate action taken. For example, if a PasswordChangeRequiredException is thrown from your LoginModule, you can catch that exception within your application, and use it to forward users to a page that allows them to change their password.

When you throw a custom exception from a LoginModule and want to catch it within your application, you must ensure that:

  1. The application catching the exception is running on the server. (Fat clients cannot catch custom exceptions.)

  2. Your servlet has access to the custom exception class at both compile time and deploy time. You can do this using either of the following methods, depending on your preference:

Method 1: Make Custom Exceptions Available via the System and Compiler Classpath
  1. Write an exception class that extends LoginException.

  2. Use the custom exception class in your classes that implement the LoginModule and AuthenticationProvider interfaces.

  3. Put the custom exception class in both the system and compiler classpath when compiling the security provider's runtime class.

  4. Generate an MBean type for your custom authentication provider, as explained in Generate an MBean Type Using the WebLogic MBeanMaker.

Method 2: Make Custom Exceptions Available via the Application Classpath
  1. Write an exception class that extends LoginException.

  2. Use the custom exception class in your classes that implement the LoginModule and AuthenticationProvider interfaces.

  3. Put the custom exception's source in the classpath of the application's build, and include it in the classpath of the application's JAR/WAR file.

  4. Generate an MBean type for your custom authentication provider, as explained in Generate an MBean Type Using the WebLogic MBeanMaker.

  5. Add the custom exception class to the MJF (MBean JAR File) generated by the WebLogic MBeanMaker.

  6. Include the MJF when compiling your application.

Example: Creating the Runtime Classes for the Sample Authentication Provider

Example 4-1 shows the SimpleSampleAuthenticationProviderImpl.java class, which is one of two runtime classes for the sample authentication provider. This runtime class includes implementations for:

  • The three methods inherited from the SecurityProvider interface: initialize, getDescription and shutdown (as described in Understand the Purpose of the Provider SSPIs.)

  • The four methods in the AuthenticationProviderV2 SSPI: the getLoginModuleConfiguration, getAssertionModuleConfiguration, getPrincipalValidator, and getIdentityAsserter methods (as described in Implement the AuthenticationProviderV2 SSPI).

    Note:

    The bold face code in Example 4-1 highlights the class declaration and the method signatures.

Example 4-1 SimpleSampleAuthenticationProviderImpl.java

package examples.security.providers.authentication.simple;
import java.util.HashMap;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
import weblogic.management.security.ProviderMBean;
import weblogic.security.spi.AuthenticationProviderV2;
import weblogic.security.spi.IdentityAsserterV2;
import weblogic.security.spi.PrincipalValidator;
import weblogic.security.spi.SecurityServices;
import weblogic.security.principal.WLSGroupImpl;
import weblogic.security.principal.WLSUserImpl;
public final class SimpleSampleAuthenticationProviderImpl implements AuthenticationProviderV2
{
   private String description;
   private SimpleSampleAuthenticatorDatabase database;
   private LoginModuleControlFlag controlFlag;
   public void initialize(ProviderMBean mbean, SecurityServices services)
   {
      System.out.println("SimpleSampleAuthenticationProviderImpl.initialize");
      SimpleSampleAuthenticatorMBean myMBean = (SimpleSampleAuthenticatorMBean)mbean;
      description = myMBean.getDescription() + "\n" + myMBean.getVersion();
      database = new SimpleSampleAuthenticatorDatabase(myMBean);
      String flag = myMBean.getControlFlag();
      if (flag.equalsIgnoreCase("REQUIRED")) {
        controlFlag = LoginModuleControlFlag.REQUIRED;
      } else if (flag.equalsIgnoreCase("OPTIONAL")) {
        controlFlag = LoginModuleControlFlag.OPTIONAL;
      } else if (flag.equalsIgnoreCase("REQUISITE")) {
        controlFlag = LoginModuleControlFlag.REQUISITE;
      } else if (flag.equalsIgnoreCase("SUFFICIENT")) {
        controlFlag = LoginModuleControlFlag.SUFFICIENT;
      } else {
        throw new IllegalArgumentException("invalid flag value" + flag);
      }
   }
   public String getDescription()
   {
      return description;
   }
   public void shutdown()
   {
      System.out.println("SimpleSampleAuthenticationProviderImpl.shutdown");
   }
   private AppConfigurationEntry getConfiguration(HashMap options)
   {
      options.put("database", database);
      return new 
        AppConfigurationEntry(
          "examples.security.providers.authentication.Simple.Simple.SampleLoginModuleImpl",
          controlFlag,
          options
        );
   }
   public AppConfigurationEntry getLoginModuleConfiguration()
   {
      HashMap options = new HashMap();
      return getConfiguration(options);
   }
   public AppConfigurationEntry getAssertionModuleConfiguration()
   {
      HashMap options = new HashMap();
      options.put("IdentityAssertion","true");
      return getConfiguration(options);
   }
   public PrincipalValidator getPrincipalValidator() 
   {
      return new PrincipalValidatorImpl();
   }
   public IdentityAsserterV2 getIdentityAsserter()
   {
      return null;
   }
}

Example 4-2 shows the SampleLoginModuleImpl.java class, which is one of two runtime classes for the sample authentication provider. This runtime class implements the JAAS LoginModule interface (as described in Implement the JAAS LoginModule Interface), and therefore includes implementations for its initialize, login, commit, abort, and logout methods.

Note:

The bold face code in Example 4-2 highlights the class declaration and the method signatures.

Example 4-2 SimpleSampleLoginModuleImpl.java

package examples.security.providers.authentication.simple;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Vector;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.spi.LoginModule;
import weblogic.management.utils.NotFoundException;
import weblogic.security.spi.WLSGroup;
import weblogic.security.spi.WLSUser;
import weblogic.security.principal.WLSGroupImpl;
import weblogic.security.principal.WLSUserImpl;
final public class SimpleSampleLoginModuleImpl implements LoginModule 
{
   private Subject subject;
   private CallbackHandler callbackHandler;
   private SimpleSampleAuthenticatorDatabase database;
   // Determine whether this is a login or assert identity 
   private boolean isIdentityAssertion;
   // Authentication status
   private boolean loginSucceeded;
   private boolean principalsInSubject;
   private Vector principalsForSubject = new Vector();
   public void initialize(Subject subject, CallbackHandler callbackHandler, Map  
   sharedState, Map options) 
   {
      // only called (once!) after the constructor and before login
      System.out.println("SimpleSampleLoginModuleImpl.initialize");
      this.subject = subject;
      this.callbackHandler = callbackHandler;
      // Check for Identity Assertion option
      isIdentityAssertion =
         "true".equalsIgnoreCase((String)options.get("IdentityAssertion"));
      database = (SimpleSampleAuthenticatorDatabase)options.get("database");
   }
   public boolean login() throws LoginException  
   {
      // only called (once!) after initialize
      System.out.println("SimpleSampleLoginModuleImpl.login");
      // loginSucceeded       should be false
      // principalsInSubject  should be false

      Callback[] callbacks = getCallbacks();
      String userName = getUserName(callbacks);
      if (userName.length() > 0) {
         if (!database.userExists(userName)) {
            throwFailedLoginException("Authentication Failed: User " + userName 
            + " doesn't exist.");
         }
        if (!isIdentityAssertion) {
         String passwordWant = null;
         try {
            passwordWant = database.getUserPassword(userName);
         } catch (NotFoundException shouldNotHappen) {}
            String passwordHave = getPasswordHave(userName, callbacks);
            if (passwordWant == null || !passwordWant.equals(passwordHave)) {
               throwFailedLoginException(
                 "Authentication Failed: User " + userName + " bad password." 
               );
            }
         }
         } else { 
          // anonymous login - let it through?
         System.out.println("\tempty userName");
         }
         loginSucceeded = true;
         principalsForSubject.add(new WLSUserImpl(userName));
         addGroupsForSubject(userName);
         return loginSucceeded;
   }
   public boolean commit() throws LoginException 
   {
      // only called (once!) after login
      // loginSucceeded      should be true or false
      // principalsInSubject should be false
      // user      should be null if !loginSucceeded, null or not-null otherwise
      // group     should be null if user == null, null or not-null otherwise

      System.out.println("SimpleSampleLoginModule.commit");
      if (loginSucceeded) {
         subject.getPrincipals().addAll(principalsForSubject);
         principalsInSubject = true;
         return true;
      } else {
         return false;
      }
   }
   public boolean abort() throws LoginException 
   {
      // The abort method is called to abort the authentication process. This is
      // phase 2 of authentication when phase 1 fails. It is called if the
      // LoginContext's overall authentication failed.
      // loginSucceeded      should be true or false
      // user      should be null if !loginSucceeded, otherwise null or not-null
      // group     should be null if user == null, otherwise null or not-null
      // principalsInSubject      should be false if user is null, otherwise true 
      //                          or false

      System.out.println("SimpleSampleLoginModule.abort");
      if (principalsInSubject) {
         subject.getPrincipals().removeAll(principalsForSubject);
         principalsInSubject = false;
      }
      return true;
   }
   public boolean logout() throws LoginException 
   {
      // should never be called
      System.out.println("SimpleSampleLoginModule.logout");
      return true;
   }
   private void throwLoginException(String msg) throws LoginException
   {
      System.out.println("Throwing LoginException(" + msg + ")");
      throw new LoginException(msg);
   }
   private void throwFailedLoginException(String msg) throws FailedLoginException
   {
      System.out.println("Throwing FailedLoginException(" + msg + ")");
      throw new FailedLoginException(msg);
   }
   private Callback[] getCallbacks() throws LoginException
   {
      if (callbackHandler == null) {
         throwLoginException("No CallbackHandler Specified");
      }
      if (database == null) {
         throwLoginException("database not specified");
      }
      Callback[] callbacks;
      if (isIdentityAssertion) {
         callbacks = new Callback[1];
      } else {
         callbacks = new Callback[2];
         callbacks[1] = new PasswordCallback("password: ",false);
      }
      callbacks[0] = new NameCallback("username: ");
      try {
          callbackHandler.handle(callbacks);
      } catch (IOException e) {
         throw new LoginException(e.toString());
      } catch (UnsupportedCallbackException e) {
         throwLoginException(e.toString() + " " + e.getCallback().toString());
      }
      return callbacks;
   }
   private String getUserName(Callback[] callbacks) throws LoginException
   {
      String userName = ((NameCallback)callbacks[0]).getName();
      if (userName == null) {
         throwLoginException("Username not supplied.");
      }
      System.out.println("\tuserName\t= " + userName);
      return userName;
   }
   private void addGroupsForSubject(String userName)
   {
      for (Enumeration e = database.getUserGroups(userName);
         e.hasMoreElements();) {
            String groupName = (String)e.nextElement();
            System.out.println("\tgroupName\t= " + groupName);
            principalsForSubject.add(new WLSGroupImpl(groupName));
      }
   }
   private String getPasswordHave(String userName, Callback[] callbacks) throws 
   LoginException
   {
      PasswordCallback passwordCallback = (PasswordCallback)callbacks[1];
      char[] password = passwordCallback.getPassword();
      passwordCallback.clearPassword();
      if (password == null || password.length < 1) {
         throwLoginException("Authentication Failed: User " + userName + ".  
            Password not supplied");
      }
      String passwd = new String(password);
      System.out.println("\tpasswordHave\t= " + passwd);
      return passwd;
   }
}

Configure the Custom Authentication Provider Using the Administration Console

Configuring a custom authentication provider means that you are adding the custom authentication provider to your security realm, where it can be accessed by applications requiring authentication services.

Configuring custom security providers is an administrative task, but it is a task that may also be performed by developers of custom security providers. This section contains information that is important for the person configuring your custom authentication providers:

Managing User Lockouts

As part of using a custom authentication provider, you need to consider how you will configure and manage user lockouts. You have two choices for doing this:

Rely on the Realm-Wide User Lockout Manager

The WebLogic Security Framework provides a realm-wide User Lockout Manager that works directly with the WebLogic Security Framework to manage user lockouts.

Note:

Both the realm-wide User Lockout Manager and a WebLogic Server 6.1 PasswordPolicyMBean (at the Realm Adapter level) may be active. See WebLogic Server API Reference Javadoc.

If you decide to rely on the realm-wide User Lockout Manager, then all you must do to make it work with your custom authentication provider is use the WebLogic Server Administration Console to:

  1. Ensure that User Lockout is enabled. (It should be enabled by default.)

  2. Modify any parameters for User Lockout (as necessary).

    Note:

    Changes to the User Lockout Manager do not take effect until you reboot the server. Instructions for using the WebLogic Server Administration Console to perform these tasks are described in Protecting User Accounts in Administering Security for Oracle WebLogic Server.

Implement Your Own User Lockout Manager

If you decide to implement your own User Lockout Manager as part of your custom authentication provider, then you must:

  1. Disable the realm-wide User Lockout Manager to prevent double lockouts from occurring. (When you create a new security realm using the WebLogic Server Administration Console, a User Lockout Manager is always created.) Instructions for performing this task are provided in Protecting User Accounts in Administering Security for Oracle WebLogic Server.

  2. Because you cannot borrow anything from the WebLogic Security Framework's realm-wide implementation, you must also perform the following tasks:

    1. Provide the implementation for your User Lockout Manager. Note that there is no security service provider interface (SSPI) provided for User Lockout Managers.

    2. Modify an MBean by which the User Lockout Manager can be managed.

    3. If you plan to manage your User Lockout Manager from the console, incorporate the User Lockout Manager into the WebLogic Server Administration Console using console extensions.

Specifying the Order of Authentication Providers

As described in LoginModules and Multipart Authentication, the order in which you configure multiple authentication providers (and thus LoginModules) affects the outcome of the authentication process.

You can configure authentication providers in any order. However, if you need to reorder your configured authentication providers, follow the steps described in Changing the Order of Authentication Providers in Administering Security for Oracle WebLogic Server.