Wednesday, July 9, 2014

ADF: Programatic user authentication instead of j_security_check (Password retrieval )

When we create ADF project and enable security, system creates a login page for us. Which has following code.

<form method="POST" action="j_security_check">
      <table cellspacing="2" cellpadding="3" border="1">
        <tr>          <th>Username:</th>
          <td>            <input type="text" name="j_username"/>          </td>
        <tr>          <th>Password:</th>
          <td>            <input type="password" name="j_password"/>          </td>
        <input type="submit" name="submit" value="Submit"/>

When user clicks on submit button, login form get submitted. It calls j_security_check, which is provided by framework. It validates username/password against ldap configured in weblogic.

We do not get any point to know what was username/password. At times we may want to derive  username/password combination. A usecase could be integration with third party. Imagine you want to integrate with third party system which takes username/password. For example sharepoint webservices. I tried checking OPSS apis but seems like there is no way to retrieve password using them. OPSS apis not retrieving password makes sense also.

To solve our problem I can think of a solution to capture user password while he is trying to login and if authentication is fine store user password in sessionscope. Use it later when calling third party system.

I personally think there might be better ways to do it but I could not find any solution. In this blog I am just describing a way to authenticate user programmatically instead of j_security_check.

Here are the steps:

1. Change login.html to login.jspx: Instead of using normal login.html page lets create a jspx page for login.
Put following form entry in it.

<af:panelFormLayout id="pfl2" styleClass="form_layout">
                        <f:facet name="footer"/>
                        <af:inputText label="#{portalBundle.USER_NAME}:" id="it1"
                                      required="true" maximumLength="50"
                        <af:inputText label="#{portalBundle.PASSWORD}:" id="it2"
                                      secret="true" required="true"
                                      maximumLength="50" partialTriggers="cb1"
                                      shortDesc="Enter Password"/>
                        <af:commandButton id="cb1"

In above code commandButton-cb1 does a partial submit and calls doLogin method of LoginPageBean. Username and password are stored in viewscope.

LoginPageBean code should look like

import com.kbace.hrc.common.util.diagnostic.DiagnosticUtil;


import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;


import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import oracle.adf.share.ADFContext;
import oracle.adf.share.logging.ADFLogger;



public class LoginPageBean {
    private static ADFLogger _logger =
    private RichOutputText pageInit;
    private RichPopup loginErrorPopup;
    private RichPopup userRoleErrorPopup;

    public LoginPageBean() {
    private String _username, _password;

       public String doLogin() {
        String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
        _username = (String)ADFUtil.evaluateEL("#{viewScope.username}");
        _password = (String)ADFUtil.evaluateEL("#{viewScope.password}");
        String un = _username;
        byte[] pw = _password.getBytes();
        FacesContext ctx = FacesContext.getCurrentInstance();
        HttpServletRequest request = (HttpServletRequest)ctx.getExternalContext().getRequest();
        String loginStatus = "login_failed";
        try {
            Subject subject =   Authentication.login(new URLCallbackHandler(un, pw));
  ,     request);

            loginStatus = "success";
             String redirecturl = "/adfAuthentication?success_url=/index.html";
            Map session = (Map)ADFUtil.evaluateEL("#{sessionScope}");
            session.put("userPassword", _password);
            session.put("username", _username);

                try {
                   FacesContext.getCurrentInstance().getExternalContext().redirect(FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath() + redirecturl);
                } catch (Exception nm) {
        } catch (FailedLoginException fle) {
            ADFUtil.getRequestFlowScope().put("pShowAuthError", "Y");
            RichPopup.PopupHints pph=new RichPopup.PopupHints();
            ADFUtil.setEL("#{viewScope.password}", null);

        } catch (LoginException le) {
            FacesMessage msg =
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error Logging In",
                                 "Error Logging In");
            ctx.addMessage(null, msg);

        return null;


Above method doLogin tries following things
1. Get username/password from viewscope
2. Authenticate username/password using weblogic security apis. 
3. If valid username/password then start a new session.
4. Store username/password in sessionscope
5. Redirect to index.html if successful login.
6. Show error popup if authentication fails.

Also we need to add following libraries in our project

a. $MW_HOME\modules
b. $MW_HOME\modules
c.       ws.api.jar: $MW_HOME\wlserver_10.3\server\lib

I am not very sure if its best way of doing it. I would definitely love to know better ways of doing this but for time being I just know this way.

Disclaimer: Any views or opinions presented in this blog are solely those of the author and do not necessarily represent those of the company.

No comments: