Programmatic login for JAAS
Programmatic login is a type of form login that supports application presentation site-specific login forms for the purpose of authentication.
When enterprise bean client applications require the user to provide identifying information, the writer of the application must collect that information and authenticate the user. The work of the programmer can be broadly classified in terms of where the actual user authentication is performed:
- In a client program
- In a server program
Users of web applications can receive prompts for authentication data in many ways. The <login-config> element in the web application deployment descriptor file defines the mechanism used to collect this information. Programmers who want to customize login procedures, rather than relying on general purpose devices like a 401 dialog window in a browser, can use a form-based login to provide an application-specific HTML form for collecting login information.
No authentication occurs unless administrative security is enabled. If we want to use form-based login for web applications, specify FORM in the auth-method tag of the <login-config> element in the deployment descriptor of each web application.
Applications can present site-specific login forms using the WebSphere Application Server form-login type. The Java EE specification defines form login as one of the authentication methods for web applications. WAS provides a form-logout mechanism.
Java Authentication and Authorization Service programmatic login
JAAS is a new feature in WAS. It is also mandated by the Java EE 1.4 Specification. JAAS is a collection of strategic authentication API that replace the Common Object Request Broker Architecture (CORBA)">Common Object Request Broker Architecture (CORBA) programmatic login APIs. WAS provides some extensions to JAAS:
Before beginning developing with programmatic login APIs, consider the following points:
- For the pure Java client application or client container application, initialize the client Object Request Broker (ORB) security prior to performing a JAAS login. Do this by running the following code prior to the JAAS login:
... import java.util.Hashtable; import javax.naming.Context; import javax.naming.InitialContext; ... // Perform an InitialContext and default lookup prior to logging // in to initialize ORB security and for the bootstrap host/port // to be determined for SecurityServer lookup. If we do not want // to validate the userid/password during the JAAS login, disable // the com.ibm.CORBA.validateBasicAuth property in the // sas.client.props file. Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory"); env.put(Context.PROVIDER_URL, "corbaloc:iiop:myhost.mycompany.com:2809"); Context initialContext = new InitialContext(env); Object obj = initialContext.lookup("");(ZOS) Note: Set com.ibm.CORBA.validateBasicAuth=false whenever connecting to a z/OS server. This function does not currently work from a distributed client to a z/OS server since the SecurityServer is located using the "UNAUTHENTICATED" principal, which is not accepted on a z/OS system.
- For the pure Java client application or the client container application, make sure that the host name and the port number of the target Java Naming and Directory Interface (JNDI) bootstrap properties are specified properly. See the Developing applications that use CosNaming (CORBA Naming interface) section for details.
- If the application uses custom JAAS login configuration, make sure that the custom JAAS login configuration is properly defined. See the Configure programmatic logins for JAAS section for details.
- Some of the JAAS APIs are protected by Java 2 security permissions. If these APIs are used by application code, make sure that these permissions are added to the application was.policy file. See Add the was.policy file to applications for Java 2 security, Use PolicyTool to edit policy files for Java 2 security and Configure the was.policy file for Java 2 security sections for details. For more details of which APIs are protected by Java 2 Security permissions, check the IBM Developer Kit, Java Technology Edition; JAAS and the WAS public APIs documentation for more details. The following list contains the APIs used in the samples code provided in this documentation.
- javax.security.auth.login.LoginContext constructors are protected by javax.security.auth.AuthPermission "createLoginContext".
- javax.security.auth.Subject.doAs and com.ibm.websphere.security.auth.WSSubject.doAs are protected by javax.security.auth.AuthPermission "doAs".
- javax.security.auth.Subject.doAsPrivileged and com.ibm.websphere.security.auth.WSSubject.doAsPrivileged are protected by javax.security.auth.AuthPermission "doAsPrivileged".
- com.ibm.websphere.security.auth.WSSubject: Due to a design oversight in JAAS Version 1.0, javax.security.auth.Subject.getSubject does not return the Subject associated with the running thread inside a java.security.AccessController.doPrivileged code block. This can present an inconsistent behavior that is problematic and causes an undesirable effort to work around. The com.ibm.websphere.security.auth.WSSubject API provides a workaround to associate the Subject to the running thread. The com.ibm.websphere.security.auth.WSSubject API extends the JAAS model to Java EE resources for authorization checks. The Subject associated with the running thread within com.ibm.websphere.security.auth.WSSubject.doAs or com.ibm.websphere.security.auth.WSSubject.doAsPrivileged code block is used for Java EE resources authorization checks.
- Administrative console support for defining new JAAS login configuration: We can configure JAAS login configuration in the administrative console and store it in the WAS configuration API. Applications can define new JAAS login configuration in the administrative console and the data is persisted in the configuration repository stored with the WAS configuration API. However, WAS still supports the default JAAS login configuration format provided by the JAAS default implementation. If duplication login configurations are defined in both the WAS configuration API and the plain text file format, the login configuration in the WAS configuration API takes precedence. Advantages to define the login configuration in the WAS configuration API include:
- Define the JAAS login configuration using the administrative console.
- Manage the JAAS login configuration centrally.
- Distributing the JAAS login configuration in a WAS ND installation.
- JAAS login configurations for WAS: WAS provides JAAS login configurations for applications to perform programmatic authentication to the WAS security runtime. These JAAS login configurations for WAS perform authentication to the configured authentication mechanism, Simple WebSphere Authentication echanism (SWAM) or LTPA, and user registry (Local OS, LDAP, or Custom) based on the authentication data supplied. The authenticated Subject from these JAAS login configurations contain the required principal and credentials that can be used by the WAS security runtime to perform authorization checks on Java EE role-based protected resources.
SWAM is deprecated in WAS v9.0 and will be removed in a future release. Here are the JAAS login configurations provided by WAS:
- WSLogin JAAS login configuration: A generic JAAS login configuration that a Java client, client container application, servlet, JSP file, enterprise bean, and so on, can use to perform authentication based on a user ID and password, or a token to the WAS security runtime. However, this configuration does not support the CallbackHandler handler specified in the client container deployment descriptor.
- ClientContainer JAAS login configuration: This JAAS login configuration recognizes the CallbackHandler handler specified in the client container deployment descriptor. The login module of this login configuration uses the CallbackHandler handler in the client container deployment descriptor if one is specified, even if the application code specified one CallbackHandler handler in the login context. This is for client container application.
- The Subjects that are authenticated with the previously mentioned JAAS login configurations contain a com.ibm.websphere.security.auth.WSPrincipal principal and a com.ibm.websphere.security.auth.WSCredential credential. If the authenticated Subject is passed to the com.ibm.websphere.security.auth.WSSubject.doAs method or the other doAs methods, the WAS security runtime can perform authorization checks on Java EE resources, based on the Subject com.ibm.websphere.security.auth.WSCredential credential.
- Customer-defined JAAS login configurations: We can define other JAAS login configurations. See Configure programmatic logins for JAAS for details. Use these login configurations to perform programmatic authentication to the custom authentication mechanism. However, the subjects from these customer-defined JAAS login configurations might not be used by the WAS security runtime to perform authorization checks if the subject does not contain the required principal and credentials.
Finding the root cause login exception from a JAAS login
If we get a LoginException exception after issuing the LoginContext.login API, we can find the root cause exception from the configured user registry. In the login modules, the registry exceptions are wrapped by a com.ibm.websphere.security.auth.WSLoginFailedException class. This exception has a getCause method with which we can pull out the exception that was wrapped after issuing the previous command.
We are not always guaranteed to get a WSLoginFailedException exception, but most of the exceptions generated from the user registry display here. The following example illustrates a LoginContext.login API with the associated catch block. Cast the WSLoginFailedException exception to com.ibm.websphere.security.auth.WSLoginFailedException class to issue the getCause API.
The following determineCause example can be used for processing CustomUserRegistry exception types.
try { lc.login(); } catch (LoginException le) { // drill down through the exceptions as they might cascade through the runtime Throwable root_exception = determineCause(le); // now we can use "root_exception" to compare to a particular exception type // for example, if we have implemented a CustomUserRegistry type, we would // know what to look for here. } /* Method used to drill down into the WSLoginFailedException to find the "root cause" exception */ public Throwable determineCause(Throwable e) { Throwable root_exception = e, temp_exception = null; // keep looping until there are no more embedded WSLoginFailedException or // WSSecurityException exceptions while (true) { if (e instanceof com.ibm.websphere.security.auth.WSLoginFailedException) { temp_exception = ((com.ibm.websphere.security.auth.WSLoginFailedException) e).getCause(); } else if (e instanceof com.ibm.websphere.security.WSSecurityException) { temp_exception = ((com.ibm.websphere.security.WSSecurityException) e).getCause(); } else if (e instanceof javax.naming.NamingException) // Check for Ldap embedded exception { temp_exception = ((javax.naming.NamingException)e).getRootCause(); } else if (e instanceof our_custom_exception_here) { // our custom processing here, if necessary } else { // this exception is not one of the types we are looking for, // lets return now, this is the root from the WebSphere // Application Server perspective return root_exception; } if (temp_exception != null) { // we have an exception; go back and see if this has another // one embedded within it. root_exception = temp_exception; e = temp_exception; continue; } else { // we finally have the root exception from this call path, this // has to occur at some point return root_exception; } } }
Finding the root cause login exception from a Servlet filter
We can also receive the root cause exception from a servlet filter when addressing post-form login processing. This exception is useful because it shows the user what happened. You can issue the following API to obtain the root cause exception:
Throwable t = com.ibm.websphere.security.auth.WSSubject.getRootLoginException(); if (t != null) t = determineCause(t);When we have the exception, we can run it through the previous determineCause example to get the native registry root cause.
Enable root cause login exception propagation to pure Java clients
Currently, the root cause does not get propagated to a pure client for security reasons. However, we might want to propagate the root cause to a pure client in a trusted environment. To enable root cause login exception propagation to a pure client, click Security > Global security > Custom Properties on the WAS Administrative Console and set the following property:
com.ibm.websphere.security.registry.propagateExceptionsToClient=true
Non-prompt programmatic login
WAS provides a non-prompt implementation of the javax.security.auth.callback.CallbackHandler interface, which is called com.ibm.websphere.security.auth.callback.WSCallbackHandlerImpl. Using this interface, an application can push authentication data to the WebSphere LoginModule instance to perform authentication. This capability is useful for server-side application code to authenticate an identity and to use that identity to invoke downstream Java EE resources.
javax.security.auth.login.LoginContext lc = null; try { lc = new javax.security.auth.login.LoginContext("WSLogin", new com.ibm.websphere.security.auth.callback.WSCallbackHandlerImpl("user", "securityrealm", "securedpassword")); // create a LoginContext and specify a CallbackHandler implementation // CallbackHandler implementation determine how authentication data is collected // in this case, the authentication data is "push" to the authentication mechanism // implemented by the LoginModule. } catch (javax.security.auth.login.LoginException e) { System.err.println("ERROR: failed to instantiate a LoginContext and the exception: " + e.getMessage()); e.printStackTrace(); // maybe javax.security.auth.AuthPermission "createLoginContext" is not granted // to the application, or the JAAS login configuration is not defined. } if (lc != null) try { lc.login(); // perform login javax.security.auth.Subject s = lc.getSubject(); // Get the authenticated subject // Invoke a Java EE resource using the authenticated subject com.ibm.websphere.security.auth.WSSubject.doAs(s, new java.security.PrivilegedAction() { public Object run() { try { bankAccount.deposit(100.00); // where bankAccount is a protected EJB } catch (Exception e) { System.out.println("ERROR: error while accessing EJB resource, exception: " + e.getMessage()); e.printStackTrace(); } return null; } } ); } catch (javax.security.auth.login.LoginException e) { System.err.println("ERROR: login failed with exception: " + e.getMessage()); e.printStackTrace(); // login failed, might want to provide relogin logic }Use the com.ibm.websphere.security.auth.callback.WSCallbackHandlerImpl callback handler with a pure Java client, a client application container, enterprise bean, JavaServer Pages (JSP) files, servlet, or other Java 2 Platform, Enterprise Edition (Java EE) resources.
The WSCallbackHandlerImpl callback handler is different depending on whether we use WAS security or Web Services Security. It is located in the sas.jar file for security, and in the was-wssecurity.jar file for Web Services Security.
User interface prompt programmatic login
WAS also provides a user interface implementation of the javax.security.auth.callback.CallbackHandler implementation to collect authentication data from a user through user interface login prompts. The com.ibm.websphere.security.auth.callback.WSGUICallbackHandlerImpl callback handler presents a user interface login panel to prompt users for authentication data.
(ZOS) (UNIX) Note: This behavior requires an X11 server to be called out by the DISPLAY environment.
javax.security.auth.login.LoginContext lc = null; try { lc = new javax.security.auth.login.LoginContext("WSLogin", new com.ibm.websphere.security.auth.callback.WSGUICallbackHandlerImpl()); // create a LoginContext and specify a CallbackHandler implementation // CallbackHandler implementation determine how authentication data is collected // in this case, the authentication date is collected by GUI login prompt // and pass to the authentication mechanism implemented by the LoginModule. } catch (javax.security.auth.login.LoginException e) { System.err.println("ERROR: failed to instantiate a LoginContext and the exception: " + e.getMessage()); e.printStackTrace(); // maybe javax.security.auth.AuthPermission "createLoginContext" is not granted // to the application, or the JAAS login configuration is not defined. } if (lc != null) try { lc.login(); // perform login javax.security.auth.Subject s = lc.getSubject(); // Get the authenticated subject // Invoke a Java EE resources using the authenticated subject com.ibm.websphere.security.auth.WSSubject.doAs(s, new java.security.PrivilegedAction() { public Object run() { try { bankAccount.deposit(100.00); // where bankAccount is a protected enterprise bean } catch (Exception e) { System.out.println("ERROR: error while accessing EJB resource, exception: " + e.getMessage()); e.printStackTrace(); } return null; } } ); } catch (javax.security.auth.login.LoginException e) { System.err.println("ERROR: login failed with exception: " + e.getMessage()); e.printStackTrace(); // login failed, might want to provide relogin logic }Do not use the com.ibm.websphere.security.auth.callback.WSGUICallbackHandlerImpl callback handler for server-side resources like enterprise bean, servlet, JSP files, and so on. The user interface login prompt blocks the server for user input. This behavior is not good for a server process.
WAS also provides a Kerberos credential cache implementation of the javax.security.auth.callback.CallbackHandler interface. The callback handler, com.ibm.websphere.security.auth.callback.WSCcacheCallBackHandlerImpl. Using this interface, an application can push authentication data to the WebSphere LoginModule instance to perform authentication.
This capability is only for the client side application code to authenticate to WAS with the Kerberos credential cache.
If the following options exist in the wsjaas_client.conf file, set them to false:
useDefaultKeytab=false
useDefaultCcache=false
tryFirstPass=false
useFirstPass=false
forwardable=false
renewable=false
renewable=false
noaddress=falsejavax.security.auth.login.LoginContext lc = null; String krb5Ccache = /etc/krb5/krb5cc_utle; try { lc = new javax.security.auth.login.LoginContext("WSKRB5Login", new com.ibm.websphere.security.auth.callback.WSCcacheCallBackHandlerImpl(user, krb5Realm, krb5Ccache, false)); // create a LoginContext and specify a CallbackHandler implementation // CallbackHandler implementation determines how authentication data is collected // in this case, the authentication date is collected by stdin prompt // and passed to the authentication mechanism implemented by the LoginModule. } catch (javax.security.auth.login.LoginException e) { System.err.println("ERROR: failed to instantiate a LoginContext and the exception: " + e.getMessage()); e.printStackTrace(); // maybe javax.security.auth.AuthPermission "createLoginContext" is not granted // to the application, or the JAAS login configuration is not defined. } if (lc != null) try { lc.login(); // perform login javax.security.auth.Subject s = lc.getSubject(); // Get the authenticated subject // Invoke a Java EE resource using the authenticated subject com.ibm.websphere.security.auth.WSSubject.doAs(s, new java.security.PrivilegedAction() { public Object run() { try { bankAccount.deposit(100.00); // where bankAccount is a protected enterprise bean } catch (Exception e) { System.out.println("ERROR: error while accessing EJB resource, exception: " + e.getMessage()); e.printStackTrace(); } return null; } } ); } catch (javax.security.auth.login.LoginException e) { System.err.println("ERROR: login failed with exception: " + e.getMessage()); e.printStackTrace(); // login failed, might want to provide relogin logic } Application Server with the default Kerberos credential cache. javax.security.auth.login.LoginContext lc = null; try { lc = new javax.security.auth.login.LoginContext("WSKRB5Login", new com.ibm.websphere.security.auth.callback.WSCcacheCallBackHandlerImpl(user, krb5Realm, null, true)); // create a LoginContext and specify a CallbackHandler implementation // CallbackHandler implementation determines how authentication data is collected // in this case, the authentication date is collected by stdin prompt // and passed to the authentication mechanism implemented by the LoginModule. } catch (javax.security.auth.login.LoginException e) { System.err.println("ERROR: failed to instantiate a LoginContext and the exception: " + e.getMessage()); e.printStackTrace(); // maybe javax.security.auth.AuthPermission "createLoginContext" is not granted // to the application, or the JAAS login configuration is not defined. } if (lc != null) try { lc.login(); // perform login javax.security.auth.Subject s = lc.getSubject(); // Get the authenticated subject // Invoke a Java EE resource using the authenticated subject com.ibm.websphere.security.auth.WSSubject.doAs(s, new java.security.PrivilegedAction() { public Object run() { try { bankAccount.deposit(100.00); // where bankAccount is a protected enterprise bean } catch (Exception e) { System.out.println("ERROR: error while accessing EJB resource, exception: " + e.getMessage()); e.printStackTrace(); } return null; } } ); } catch (javax.security.auth.login.LoginException e) { System.err.println("ERROR: login failed with exception: " + e.getMessage()); e.printStackTrace(); // login failed, might want to provide relogin logic } Application Server with the Microsoft native Kerberos credential cache. The client must login to the Microsoft Domain Controller. javax.security.auth.login.LoginContext lc = null; try { lc = new javax.security.auth.login.LoginContext("WSKRB5Login", new com.ibm.websphere.security.auth.callback.WSCcacheCallBackHandlerImpl(null, null, null, true)); // create a LoginContext and specify a CallbackHandler implementation // CallbackHandler implementation determines how authentication data is collected // in this case, the authentication date is collected by stdin prompt // and passed to the authentication mechanism implemented by the LoginModule. } catch (javax.security.auth.login.LoginException e) { System.err.println("ERROR: failed to instantiate a LoginContext and the exception: " + e.getMessage()); e.printStackTrace(); // maybe javax.security.auth.AuthPermission "createLoginContext" is not granted // to the application, or the JAAS login configuration is not defined. } if (lc != null) try { lc.login(); // perform login javax.security.auth.Subject s = lc.getSubject(); // Get the authenticated subject // Invoke a Java EE resource using the authenticated subject com.ibm.websphere.security.auth.WSSubject.doAs(s, new java.security.PrivilegedAction() { public Object run() { try { bankAccount.deposit(100.00); // where bankAccount is a protected enterprise bean } catch (Exception e) { System.out.println("ERROR: error while accessing EJB resource, exception: " + e.getMessage()); e.printStackTrace(); } return null; } } ); } catch (javax.security.auth.login.LoginException e) { System.err.println("ERROR: login failed with exception: " + e.getMessage()); e.printStackTrace(); // login failed, might want to provide relogin logic }
WSKRB5Login module
The WSKRB5Login JAAS login configuration: is a generic JAAS login configuration that a Java client, client container application, servlet, JSP file, or enterprise bean can use to perform authentication based on a Kerberos principal name password or a Kerberos credential cache to the WAS security runtime. However, this configuration does not support the CallbackHandler handler specified in the client container deployment descriptor.
Place either the krb5.ini or krb5.conf files we have created in a default location. If either file is not located in the default location set the java.security.krb5.conf JVM system property with the correct path and Kerberos configuration file name.
On a Windows platform, the default location is c:\winnt\krb5.ini.
On a Linux platform, the default location is /etc/krb5.conf.
On other Unix platforms, the default location is /etc/krb5/krb5.conf.
On a z/OS platform, the default location is /etc/krb5/krb5.conf.
Kerberos configuration settings, the Kerberos key distribution center (KDC) name, and realm settings are provided in the Kerberos configuration file or through java.security.krb5.kdc and java.security.krb5.realm system property files.
Stdin prompt programmatic login
WAS also provides a stdin implementation of the javax.security.auth.callback.CallbackHandler interface. The callback handler, com.ibm.websphere.security.auth.callback.WSStdinCallbackHandlerImpl, prompts and collects authentication data from a user through the stdin prompt.
javax.security.auth.login.LoginContext lc = null; try { lc = new javax.security.auth.login.LoginContext("WSLogin", new com.ibm.websphere.security.auth.callback.WSStdinCallbackHandlerImpl()); // create a LoginContext and specify a CallbackHandler implementation // CallbackHandler implementation determines how authentication data is collected // in this case, the authentication date is collected by stdin prompt // and passed to the authentication mechanism implemented by the LoginModule. } catch (javax.security.auth.login.LoginException e) { System.err.println("ERROR: failed to instantiate a LoginContext and the exception: " + e.getMessage()); e.printStackTrace(); // maybe javax.security.auth.AuthPermission "createLoginContext" is not granted // to the application, or the JAAS login configuration is not defined. } if (lc != null) try { lc.login(); // perform login javax.security.auth.Subject s = lc.getSubject(); // Get the authenticated subject // Invoke a Java EE resource using the authenticated subject com.ibm.websphere.security.auth.WSSubject.doAs(s, new java.security.PrivilegedAction() { public Object run() { try { bankAccount.deposit(100.00); // where bankAccount is a protected enterprise bean } catch (Exception e) { System.out.println("ERROR: error while accessing EJB resource, exception: " + e.getMessage()); e.printStackTrace(); } return null; } } ); } catch (javax.security.auth.login.LoginException e) { System.err.println("ERROR: login failed with exception: " + e.getMessage()); e.printStackTrace(); // login failed, might want to provide relogin logic }Do not use the com.ibm.websphere.security.auth.callback.WSStdinCallbackHandlerImpl callback handler for server-side resources like enterprise beans, servlets, JSP files, and so on. The input from the stdin prompt is not sent to the server environment. Most servers run in the background and do not have a console. However, if the server does have a console, the stdin prompt blocks the server for user input. This behavior is not good for a server process.
Developing programmatic logins with the JAAS Developing custom login modules for a system login configuration for JAAS Customize a server-side JAAS authentication and login configuration Getting the caller subject from the thread for JAAS Getting the RunAs subject from the thread for JAAS Overriding the RunAs subject on the thread for JAAS Revoking users from a cache for JAAS