Jonas Flesch
Jonas Flesch

Reputation: 309

Which is the best way to authenticate through Active Directory with Single Sign On when available?

We need to authenticate through Active Directory. We would like if our Windows users inside the domain could authenticate without putting in username and password (Single Sign On), but also external users (or users not using Internet Explorer) being able to insert their username and password and login.

We also need to put out hands in the groups that the user is member, because this will change what this user will be able to see in our website.

We are using Java with Jetty as our application server, and developing in Windows but our server will be Linux.

Thank you!

Upvotes: 2

Views: 2178

Answers (3)

José F. Romaniello
José F. Romaniello

Reputation: 14156

As suggested by @Akber you can use the IP range. You need a public endpoint that will use the remote address or the X-Forwarded-For header, with the IP you can test if it is inside the intranet range, this is 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/12.

If the IP is in the intranet range you can redirect to an Apache Proxy (more on this later). If the IP is out of the range you redirect to an endpoint with a nice looking form.

Integrated Authentication Endpoint

Apache with mod_auth_kerb is one of the only ways that have worked for us in Linux for this scenario. You can configure apache as a kerberos proxy, it will negotiate kerberos and then call your backend with a header. This is an example piece of configuration:

  ProxyPass        / http://localhost:9005/ #your backend
  ProxyPassReverse / http://localhost:9005/ #your backend
  ProxyPreserveHost On

  ## Rewrite rules
  RewriteEngine On
  RewriteCond %{LA-U:REMOTE_USER} (.+)
  RewriteRule . - [E=RU:%1]

  ## Request header rules
  ## as per http://httpd.apache.org/docs/2.2/mod/mod_headers.html#requestheader
  RequestHeader set X-Forwarded-User %{RU}e

  <Location />
     AuthName "Kerberos Login"
     AuthType Kerberos
     Krb5Keytab /path/to your keytab/HTTP.keytab
     KrbAuthRealm DOMAIN.LOC
     KrbMethodNegotiate on
     KrbSaveCredentials off
     KrbVerifyKDC off
     KrbServiceName HTTP/YOURAPP.AD2008R2.LOC
     Require valid-user
  </Location>

Then you backend will receive the X-Forwarded-User and you can use LDAP to fetch the full profile and the groups recursively.

Notice there is a /path/to your keytab/HTTP.keytab, this file should be generated from a Windows machine bound to the domain.

Form Authentication Endpoint

This is handled directly by your application, once you receive and username and password you will have to try to "bind" to AD using the LDAP protocol, then you have to fetch the user profile and groups recursively.

Cons of this approach and alternative solutions

It might seem simple, but it actually involves a lot of work on your side, not only code but maintenance as well. There are other two solutions that might work in your case but it requires to deploy another product;

  • ADFS: is a product from Microsoft that can be deployed in a Windows Server (IIS), authenticates with AD and talks WS-Federation or SAML.
  • Auth0: can be deployed on premises, it is provided as Virtual Appliance (linux). It can authenticate using any identity provider, including AD of course. We do something similar to what I've described here for AD but from your application you don't need to do anything, you simple use an OAuth library or just JWT validation library.

Disclaimer: I work for Auth0.

Upvotes: 3

StoopidDonut
StoopidDonut

Reputation: 8617

If the external users can be authenticated, you might have a requirement of storing the usernames (atleast) within your application database.

You can get the user group information through AD implementation of LDAP using JNDI (which would require a few standard entries to your web.xml too).

You can use NTLM authentication through JCIFS for internal AD users on your intranet.

Based on this assumption:

  1. Search for NTLMFilter class, there is a common code which can be used
  2. Use this NTLMFilter implementation created in step 1 for single sign on for the user within the intranet using NTLM authentication through JCIFS: like this

    public static Boolean authenticateUsingJCIF (String username, String password) {

            UniAddress uniaddress = null;
            String _methodName = "authenticateUsingJCIF";
                    try {
                uniaddress = UniAddress.getByName(PropertyUtils
                        .getProperty(AUTHENTICATION_SERVER_URL));
                NtlmPasswordAuthentication ntlmpasswordauthentication = new NtlmPasswordAuthentication(PropertyUtils.getProperty(AUTHENTICATION_SERVER_DOMAIN),username, password); //You can have your own method to read properties, I've just delegated to a generic utils
                SmbSession.logon(uniaddress, ntlmpasswordauthentication);
                logger.info("INTERNAL User authenticated successfully against AD");
            } catch (UnknownHostException e) {
                logger.error(e.toString(), e);
                return false;
            } catch (SmbException e) {
                logger.error(e.toString(), e);
                return false;
            } catch (Exception e) {
                logger.error(e.toString(), e);
                return false;
            }
            return true;
        }
    
  3. For external users have a normal username/password validation: like this

    private String authenticateExternalUser(String id, YourUserNamePasswordAuthenticationSource authSource) { String statusMessage = null;

        logger
                .info("Performing credential verification for external user against INTERNAL DB");
            //Create or utilize your own handlers to validate plain/encoded credentials against internal db
        CredentialVerificationResult returnCode = _handler
                .verifyInternalCredential(id, authSource.getPassword());
        logger.info("Logging value of return code" + returnCode.getMessage());
        if (returnCode == CredentialVerificationResult.SUCCESS) {
            statusMessage = CREDENTIAL_VERIFICATION_SUCCESS;
        } else if (returnCode == CredentialVerificationResult.BAD_USER_ID) {
            statusMessage = BAD_USER_ID;
        } else if (returnCode == CredentialVerificationResult.WAIT_TO_RETRY) {
            statusMessage = WAIT_TO_RETRY;
        } else if (returnCode == CredentialVerificationResult.CREDENTIAL_LOCKED) {
            statusMessage = CREDENTIAL_LOCKED;
        } else if (returnCode == CredentialVerificationResult.PASSWORD_MISMATCH) {
            statusMessage = PASSWORD_MISMATCH;
        }
        return statusMessage;
    }
    

You might want to have a common method to validate internal/external users (to invoke the above 2 methods) and return your results accordingly.

Hope it helps, and good luck!

Upvotes: 0

Akber Choudhry
Akber Choudhry

Reputation: 1785

For group personalisation, the web app would need to know where a user came from (Intranet or Internet) and that can be done through headers, IP range configuration in Apache etc. -- it depends on your detailed configuration and code.

For Intranet SSO, it appears that you need SPNEGO, link below. For Intranet SSO, your company's administration policies and Internet Explorer settings will play a big role, so I am copying the checklist from the page:

  1. Accessing the server using a Hostname rather then IP Integrated
  2. Windows Authentication in IE is enabled, the host is trusted in
  3. Firefox The Server is not local to the browser The client's Kerberos
  4. system is authenticated to a domain controller

http://wiki.eclipse.org/Jetty/Howto/Spnego

Upvotes: 0

Related Questions