0

DefaultHttpHandler is deprecated, HttpURLConnection does not support NTLM and NTLM seems to be the only well-supported protocol by ASP.NET MCV websites. So, what's left to do?

In our business we use Microsoft. We log in using Microsoft, our webmail is done by Microsoft, and our tablet applications are Android...

Currently I'm working on a project that requires a connection to an ASP.NET entity framework webinterface. This webinterface is hosted on an IIS, configured with Windows Authentication, using NTLM as provider.

The app I'm making has to access this webinterface. So, I ask the users for their username and password, and want to log in on the webinterface. However, Android does not support NTLM at all. I've been looking around, but it seems like this combination is fairly rare. I'd like to know, what are my options?

I am allowed to mess with pretty much anything. The only requrement is that users log in using their Microsoft account, and we'd prefer not using the overly-priced Xamarin. What would you reccomend?

Daniël van den Berg
  • 2,197
  • 1
  • 20
  • 45
  • IMO, you can read [my answer here](http://stackoverflow.com/questions/31466653/how-to-use-security-authentication-authorization-in-asp-web-api/31471027#31471027) – BNK Sep 07 '15 at 09:22
  • Check out the JCIFS jar. Used it to get our NTLM up and running. If you need help I will post how I did it here!. – Smashing Sep 07 '15 at 10:02
  • @Smashing I've seen JCIFS come by a couple times, but as far as I know only with the DefaultHttpHandler. I'll look into it a bit more, but if you could provide me an example on how to use it with HttpURLConnection that'd be great. – Daniël van den Berg Sep 07 '15 at 10:11
  • As far as I know you cannot use HttpURLConnection, we still use DefaultHttpHandler even though it might be deprecated. Still haven't found a workaround :(. – Smashing Sep 07 '15 at 10:15
  • @BNK In your answer I do unfortunately not see any code that would be useful to me. Maybe the concept could be, but I don't see how to get past the limitations of HttpURLConnection then. – Daniël van den Berg Sep 07 '15 at 11:12
  • @Smashing About 3 hours and 0 lines of code further.... I think I'd love to see your solution. – Daniël van den Berg Sep 07 '15 at 12:06
  • There you go. Let me know if you have any questions – Smashing Sep 07 '15 at 12:20
  • I mean that you implement a WebApi that uses Windows authentication, Android client firstly sends user & pass to get the token from Web API. From then, Android uses token instead of user&pass. – BNK Sep 07 '15 at 12:22
  • An example using HttpURLConnection : http://stackoverflow.com/a/34321230/2073804 – ron190 Dec 20 '15 at 02:24

2 Answers2

0

Okay so the first thing I did was to import the JCIFS library. Its a jar you download from that link.

The next thing I needed to do was to import the JCIFSEngine class and NTLMSchemeFactory class into your project.

Then finally this method builds your HTTPClient:

//I know it is deprecated but there is no other way to implement NTLM thus far.
    private static DefaultHttpClient setupHttpClient (String username, String password) {
        DefaultHttpClient httpclient = new DefaultHttpClient();
        // register ntlm auth scheme
        httpclient.getAuthSchemes().register("ntlm", new NTLMSchemeFactory());
        httpclient.getCredentialsProvider().setCredentials(
                // Limit the credentials only to the specified domain and port
                new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
                // Specify credentials, most of the time only user/pass is needed
                new NTCredentials(username, password, "", "")
        );
        return httpclient;
    }

My connections are done with Retrofit so I just attach this HTTPClient to retrofit using the following line:

retrofitAdapterBuilder.setClient(new ApacheClient(setupHttpClient(username, password)));

This worked for me thus far even-though it is really bad that Android has no native support for this.

EDIT: Adding an Okhttp implementation. This is an old class and we are no longer using it as we decided to drop NTLM support, but it used to work. Please keep in mind that we connect to servers that can have basic/windows/azure authentication so we need to do multiple checks which you might not need to do.

First create a class which implements the Authenticator interface from okhttp. Then in the authenticate() method do the following:

    @Override
public Request authenticate(Route route, okhttp3.Response response) throws IOException {
    if (response == null || response.headers() == null || response.headers().toString().isEmpty()) {
        return null;
    }

    final List<String> authHeaders = response.headers().values("WWW-Authenticate");

    if (authHeaders.contains("NTLM")) {
        return response.request().newBuilder().header("Authorization", "NTLM " + mNtlmInitialChallenge).build();
    } else if (checkIsBasicAuth(authHeaders)) {
        String credentials = mUsername + ":" + mPassword;
        final String basic = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
        return response.request().newBuilder().header("Authorization", basic).build();
    }

    if (mChallengeSent) {
        return null;
    } else {
        mChallengeSent = true;
        String ntlmFinalChallenge = null;
        try {
            ntlmFinalChallenge = mNtlmEngine.generateType3Msg(mUsername, mPassword, mDomain, "android-device", authHeaders.get(0).substring(5));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return response.request().newBuilder().header("Authorization", "NTLM " + ntlmFinalChallenge).build();
    }
}

To generate the mNtlmInitialChallenge create the following method:

    private String getInitialNtlmChallenge() {
    String initialChallenge = null;
    try {
        initialChallenge = mNtlmEngine.generateType1Msg(null, null);
    } catch (Exception e) {
        e.printStackTrace();
    }

    return initialChallenge;
}

The mNtlmEngine class I copied over from the JCIFS library manually so I could remove the dependency.

Some notes:

  1. The code is not the cleanest as this was a POC.

  2. Yes we used the dreaded mVariable notation, we are not using it anymore.

  3. You are going to have to play around with this implementation until it works for you.

  4. To attach the authenticator to okhttp just do client.authenticator(new NtlmAuthenticator(username, password, ""));

  5. You are probably going to need to implement some failure mechanism.

Community
  • 1
  • 1
Smashing
  • 1,640
  • 18
  • 16
  • @RonRon Thanks for the comment. I will surely look into it!. – Smashing Dec 21 '15 at 07:37
  • I have added the above mention code and library and both classes in package but I am getting 401 response and my credentials are correct I check that already please can you help me to solve this – alex Jan 15 '16 at 07:05
  • 1
    I'm currently working on implementing this with Okhttp. Will anybody be interested in seeing the implementation?. – Smashing Feb 10 '16 at 11:24
  • @Smashing Do you have such an implementation? – IgorGanapolsky Jun 13 '18 at 18:15
  • @IgorGanapolsky I added the implementation we briefly used until we decided to dropped ntlm support to move to a cloud oriented approach. – Smashing Jun 14 '18 at 04:37
0

Just do FBA in MCV https://msdn.microsoft.com/en-us/library/ff398049(v=vs.100).aspx

It is well supported.

markgamache
  • 436
  • 2
  • 6
  • This would indeed make it easy for the application to connect. However, as far as I know this does not use the active directory but a seperate table. We'd prefer not having to make our employees register themselves in different places. Maybe to someone else this would be a solution, but unfortunately not to me. – Daniël van den Berg Sep 08 '15 at 06:00
  • You can point FBA username and password anywhere. Use the MS DE class https://msdn.microsoft.com/en-us/library/System.DirectoryServices.DirectoryEntry(v=VS.110).aspx – markgamache Sep 08 '15 at 15:04