Waffle : Windows Single Sign On

Last week, I was working on an application where I had to do LDAP authentication. My cmpany has been using a very old jar file that had the required code for authenticating and authorizing users. There were two problems for me.
1. I had to revisit the documentation on LDAP setting in Jboss 4 and figure how to do the set up. Making changes in login-config.xml etc.
2. My new application is using Spring-boot, which comes with built in tomcat libraries. I had to either find some documentation on Timcat with Spring boot 4 or build the app as war and deploy in Jboss 7 and not Jboss 4, which means going over documentation and tutorial.

What is waffle?

While trying to figure out a lazy way of doing things, I ran into Waffle project. WAFFLE is a native Windows Authentication Framework consisting of two C# and Java libraries that perform functions related to Windows authentication, supporting Negotiate, NTLM and Kerberos. Waffle also includes libraries that enable drop-in Windows Single Sign On for popular Java web servers, when running on Windows. While Waffle makes it ridiculously easy to do Windows Authentication in Java, on Windows, Waffle does not work on *nix. In Short, if a user logs into his company computer, he would be authenticating over LDAP to make sure that he is authorized to use the computer. Waffle gives you an opportunity to make use of the exixsting infrastrutuce and help you authenticate and authorzie the user.

Set Up::

To use waffle in java, we just need to add the maven dependency:

<properties>
<waffle.version>1.5</waffle.version>
</properties>

<dependency>
<groupId>com.github.dblock.waffle</groupId>
<artifactId>waffle-jna</artifactId>
<version>${waffle.version}</version>
</dependency>

<dependency>
 <groupId>net.java.dev.jna</groupId>
 <artifactId>platform</artifactId>
 <version>3.5.2</version>
 </dependency>

Create a new instance of waffle.windows.auth.impl.WindowsAuthProviderImpl.
Here are a couple of scenario that you can do without any nasty setup in Java, Jboss, Tomcat etc.

Scenario 1: Logon a user and get his domain and local groups

This calls Win32-LogonUser, checks the user token and extracts all local domain information.

IWindowsAuthProvider prov = new WindowsAuthProviderImpl();
IWindowsIdentity identity = prov.logonUser("userName", "pwd");
System.out.println("User identity: " + identity.getFqn());
for(IWindowsAccount group : identity.getGroups()) {
System.out.println(" " + group.getFqn() + " (" + group.getSidString() + ")");
}

Output::

User identity: acnna\darora
acnna\\None (S-1-5-21-42300245183-42300245183-3597729351-513)
Everyone (S-1-1-0)
acnna\\HomeUsers (S-1-5-21-42300245183-42300245183-3597729351-2418)
BUILTIN\Administrators (S-1-5-32-544)
BUILTIN\Users (S-1-5-32-545)
NT AUTHORITY\NETWORK (S-1-5-2)

Scenario 2: Active directory: get the list of trusted domains

IWindowsAuthProvider prov = new WindowsAuthProviderImpl();
IWindowsDomain[] domains = prov.getDomains();
for(IWindowsDomain domain : domains) {
System.out.println(domain.getFqn() + ": " + domain.getTrustDirectionString());
}

Output::

Scenario 3:: Is computer active on a domain?


IWindowsAuthProvider prov = new WindowsAuthProviderImpl();
IWindowsComputer computer = prov.getCurrentComputer();
System.out.println(computer.getComputerName());
System.out.println(computer.getJoinStatus());
System.out.println(computer.getMemberOf());

For systems that run both with and without active directory you need to programmatically figure out whether a computer is joined to a domain or a workgroup. If it’s joined to a domain or a workgroup you want to know what domain the computer is joined to.

Scenario 4: Negotiate Single Sign on

This lets both client and server side to negotatite the sign on protocol. The code below, gets the token which can be shared between client and server and be used to lookup its domains.

String securityPackage = "Negotiate";
// client credentials handle
IWindowsCredentialsHandle clientCredentials = WindowsCredentialsHandleImpl.getCurrent(securityPackage);
clientCredentials.initialize();
// initial client security context
WindowsSecurityContextImpl clientContext = new WindowsSecurityContextImpl();
clientContext.setPrincipalName(Advapi32Util.getUserName());
clientContext.setCredentialsHandle(clientCredentials.getHandle());
clientContext.setSecurityPackage(securityPackage);
clientContext.initialize();
// accept on the server
WindowsAuthProviderImpl provider = new WindowsAuthProviderImpl();
IWindowsSecurityContext serverContext = null;
do
{
if (serverContext != null) {
// initialize on the client
SecBufferDesc continueToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, serverContext.getToken());
clientContext.initialize(clientContext.getHandle(), continueToken);
}
// accept the token on the server
serverContext = provider.acceptSecurityToken(clientContext.getToken(), securityPackage);
} while (clientContext.getContinue() || serverContext.getContinue());

System.out.println(serverContext.getIdentity().getFqn());
for (IWindowsAccount group : serverContext.getIdentity().getGroups()) {
System.out.println(” ” + group.getFqn());
}

serverContext.dispose();
clientContext.dispose();
clientCredentials.dispose();

 

~Ciao

  • Martin

    Could you also provide a hint how to make these snippets work within the context of authorization in a spring-boot application? That would be very helpful. Thanks.

    • Dinesh

      Here’s a small snippet.

      // client credentials handle
      IWindowsCredentialsHandle credentials= WindowsCredentialsHandleImpl.getCurrent(“Negotiate”);
      credentials.initialize();

      // initial client security context
      WindowsSecurityContextImpl clientContext = new WindowsSecurityContextImpl();
      clientContext.setPrincipalName(Advapi32Util.getUserName());
      clientContext.setCredentialsHandle(credentials.getHandle());
      clientContext.setSecurityPackage(securityPackage);
      clientContext.initialize();

      // accept on the server
      WindowsAuthProviderImpl provider = new WindowsAuthProviderImpl();
      IWindowsSecurityContext serverContext = null;

      do {

      if (serverContext != null) {

      // initialize on the client
      SecBufferDesc continueToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, serverContext.getToken());
      clientContext.initialize(clientContext.getHandle(), continueToken);
      }

      // accept the token on the server
      serverContext = provider.acceptSecurityToken(clientContext.getToken(), “Negotiate”);

      } while (clientContext.getContinue() || serverContext.getContinue());

      System.out.println(serverContext.getIdentity().getFqn());
      for (IWindowsAccount group : serverContext.getIdentity().getGroups()) {
      System.out.println(” ” + group.getFqn());
      }

  • moshe

    Where do you find WindowsSecurityContextImpl.Initliaze that does not take parameters ? there is no such class.

  • andreas

    I have the same problem with the empty initialize() of WindowsSecurityContext class and I use jna-waffle-1.7.3 as well.

    The source on https://github.com/dblock/waffle/blob/master/Source/JNA/waffle-jna/src/main/java/waffle/windows/auth/impl/WindowsSecurityContextImpl.java does not contain an empty initialize() method.

    Or am I blind???

    Thanks for help. The snippet has more errors in my eclipse, here is the full snippet:

    import waffle.windows.auth.IWindowsAccount;
    import waffle.windows.auth.IWindowsCredentialsHandle;
    import waffle.windows.auth.IWindowsSecurityContext;
    import waffle.windows.auth.impl.WindowsAuthProviderImpl;
    import waffle.windows.auth.impl.WindowsCredentialsHandleImpl;

    import waffle.windows.auth.impl.WindowsSecurityContextImpl;

    import com.sun.jna.platform.win32.Advapi32Util;
    import com.sun.jna.platform.win32.Sspi;
    import com.sun.jna.platform.win32.Sspi.SecBufferDesc;

    public class NegotiateSample {

    public static void main(String[] args) {
    String securityPackage = “Negotiate”;
    // client credentials handle
    IWindowsCredentialsHandle clientCredentials = WindowsCredentialsHandleImpl.getCurrent(securityPackage);
    clientCredentials.initialize();
    // initial client security context
    WindowsSecurityContextImpl clientContext = new WindowsSecurityContextImpl();
    clientContext.setPrincipalName(Advapi32Util.getUserName());
    clientContext.setCredentialsHandle(clientCredentials.getHandle());
    clientContext.setSecurityPackage(securityPackage);
    clientContext.initialize();
    // accept on the server
    WindowsAuthProviderImpl provider = new WindowsAuthProviderImpl();
    IWindowsSecurityContext serverContext = null;
    do
    {
    if (serverContext != null) {
    // initialize on the client
    SecBufferDesc continueToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, serverContext.getToken());
    clientContext.initialize(clientContext.getHandle(), continueToken);
    }
    // accept the token on the server
    serverContext = provider.acceptSecurityToken(clientContext.getToken(), securityPackage);
    } while (clientContext.getContinue() || serverContext.getContinue());

    System.out.println(serverContext.getIdentity().getFqn());
    for (IWindowsAccount group : serverContext.getIdentity().getGroups()) {
    System.out.println(” ” + group.getFqn());
    }

    serverContext.dispose();
    clientContext.dispose();
    clientCredentials.dispose();
    }

    }

    • Dinesh

      I think the problem is that you are using a different version that mine. I am using this dependency.

      net.java.dev.jna
      platform
      3.5.2


      com.github.dblock.waffle
      waffle-jna
      1.5

  • hi, please can you share your contact detail

    • Dinesh

      Is there something specific that you want to know about this post? I reply within 24 hr on my comments.

  • Hi Dinesh,

    Can this be done for the mac users also . is there any jar files for that.

    • Dinesh

      I am not sure, but I guess it should work.

  • ⴰⵖⵉⵍⴰⵙ ⵉⵎⴰⵣⵉⵖⴻⵏ

    Hi Dinesh,

    And how we can use them with spring security ?