javaauthenticationsingle-sign-onwildflywaffle

Wildfly 10 Windows authentication


I got stuck on servlet's and security filter for a java web application. so I got my web.xml which looks like this:

<!-- <distributable/> -->

<filter>
<filter-name>com.company.xxx.xxx.xxx.SecurityFilter</filter-name>
<filter-class>com.company.xxx.xxx.xxx.SecurityFilter</filter-class>
</filter>

<filter>
<filter-name>WaffleSSOFilter</filter-name>
<filter-class>waffle.servlet.NegotiateSecurityFilter</filter-class>
<init-param>
  <param-name>securityFilterProviders</param-name>
  <param-value>
      waffle.servlet.spi.NegotiateSecurityFilterProvider
  </param-value>
 </init-param>
 <init-param>
  <param-name>allowGuestLogin</param-name>
  <param-value>false</param-value>
 </init-param>
 <init-param>
  <param-name>waffle.servlet.spi.NegotiateSecurityFilterProvider/protocols</param-name>
  <param-value>
      Negotiate
  </param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>com.company.xxx.xxx.xxx.SecurityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
<filter-name>WaffleSSOFilter</filter-name>
<url-pattern>/xxx/xxx/xxx/windowsLogin</url-pattern>
</filter-mapping>

<!-- Enabling it disables access to App from other computers -->
<context-param>
<param-name>org.jboss.weld.development</param-name>
<param-value>false</param-value>
</context-param>

<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/xxx/*</url-pattern>
</servlet-mapping>

</web-app>

I got this setup because I stumbled across this little post: Multiple filters with same url mapping

and then I found this post which helped me to get in the correct direction:java-sso-with-wildfly-8-java-1-8-0-45-and-active-directory

so I try to access this part right here throug the filter WaffleSSOFilter.

public String getUserName( HttpServletRequest servletRequest )
  {
    Enumeration<String> headerNames = servletRequest.getHeaderNames();
    while ( headerNames.hasMoreElements() )
    {
      String headerName = headerNames.nextElement();
      String headerValue = servletRequest.getHeader( headerName );
      log.info( "Header Name:" + headerName + " " + headerValue );
    }
    String remotePrincipal = servletRequest.getRemoteUser();
    log.info( "REMOTE USER: " + remotePrincipal );
    log.info( "PRINCIPAL: " + servletRequest.getUserPrincipal().toString() );
    return remotePrincipal;
  }

The difficult part on this for me is that the first filter "com.company.xxx.xxx.xxx.SecurityFilter" is needed on every URL, because our application is broken without it and nothing works then. but I need a special POST URL for my WaffleSSOFilter to enable the ability to authenticate over the logged in windows user on the remote machine accessing the website.

The goal is to have the login page with the normal username and password form and have a checkbox to enable the windows authentication.

With this setup I can login normally with username and password. the application is working, which is good so far. Now, if I make a POST request to my special URL to test the windows authentication, I get a java.lang.NullPointerException on the previous source code excactly at accessing servletRequest.getUserPrincipal().toString()

Question: Where is my error in the filter setup or what is wrong with the source code?

PS: Yes I configured my browsers to enable the request as mentioned in the link on the second post. PPS: When I remove our first filter and route everything through the waffle filter the login works and I don't get a NullPointerException, the application is completely broken though.


Solution

  • Alright so I kept diging and after a night of sleep I thought about trying something new.

    Since I got the problem of already having a custom securityFilter that blocks waffle to perform correctly (even with filter jumping with dispatcher etc). My eyes caught something interesting.

    Waffle set a Header with the name "WWW-Authenticate", so why not try around in that direction. after a couple of hours I got it working. I am performing my own NTLM handshake and read out what I need from the token. get username from NTLM auth header helped my quite a lot in understanding how to perform my own NTLM handshake. Basically I customized the source code found there with the help of this masterpeace NTLM Authentication Scheme for HTTP. Now after seeing how the tokens and packages are built I digged into it and forced my server to requesting this authentication and then after getting the NTLM V3 token, reading out what I need to log in the user.

    With this solution I can keep my structure. Only one filter, no wildfly customization but just pure web app logic to get it working.

    What is important, is that I still need to configurate the browsers to trust the website I browse, so I don't get the popup asking for credentials. This can be found here Waffle Doc but I had to add network.negotiate-auth.trusted-uris for firefox and then add the domain (ex: http://localhost) to get the popup to dissapear.

    Just thought I would share this if somebody has a similar problem with that sorrounding.