waffle icon indicating copy to clipboard operation
waffle copied to clipboard

Using WAFFLE from a servlet and subsequently passing the authorization to another resource using HTTPClient from a servlet

Open MMirabito opened this issue 5 years ago • 8 comments

Dear all,

I have the following use case but I am not sure if WAFFLE can help in resolving it. I am using Waffle 2.1.0 and immediately below s my high-level scenario:

Browser -> Web App with WAFFLE Filter -> Servlet uses HTTPClient -> Additional resource (authentication required)

I have a simple web app running on a windows sever which is running under Tomcat 9.x on JDK 1.8- 231. In the web app I use WAFFLE so that the user can be authenticated.

  1. Once the authentication process is complete the servlet needs to execute a call using the Apache HttpClient request to another server within our domain but as of a few days ago the URL resource is now requiring the request to be authenticated. For context this was recently implemented as a requirement by my organization. Therefore, while in the past anonymous access would have worked with HTTPClient just fine, now it is failing with unauthorized access.

  2. I was able to find a workaround by using a domain service account and create an NTCredential object which I pass to the HTTPClient (I found a few examples on the net that guided me through it java-httpclient-doesnt-authenticate-with-ntlm ) see code at end of post. And While this option works it is somewhat clumsy as it requires that I manage a domain account and its credentials, which means when the credentials expire I need to handle the change.

  3. Ideally I would like to tell HTTPClient to use or piggy back on the authentication and authorization that just occurred between the user browser and the servlet since the user that is logged into the domain will also have access to that resource that HTTPClinet will need to access. However, I am not sure if this is even possible or how I would to do it with WAFFLE , HTTPClient and a severlet - I think it might be referred to as pass-thru authentication, or preemptive authentication or trusted for delegation - but I am not sure - Ii am new to this so I am confused about my options.

Does anyone have any suggestions? Thank in advance for any guidance.

max

`

public static Map<String, Object> getResourceByUrl(String url) {
    
    Properties props = Utils.getAppProperties();
    String userId = props.getProperty("domain.id");
    String password = props.getProperty("domain.pwd");
    String domain = props.getProperty("domain.name");
    
    Map<String, Object> map = new HashMap<String, Object>();
    
    NTCredentials credentials = new NTCredentials(userId, password, null, domain);
    CredentialsProvider credsProvider = new BasicCredentialsProvider();
    credsProvider.setCredentials(AuthScope.ANY, credentials);

    RequestConfig requestConfig = RequestConfig.custom()
            .setSocketTimeout(30000)
            .setConnectTimeout(30000)
            .setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM))
            .setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
            .build();
    
    HttpClient client = HttpClientBuilder.create()
            .setDefaultCredentialsProvider(credsProvider)
            .setDefaultRequestConfig(requestConfig)
            .build();
    
    try {
        HttpResponse response =  client.execute(new HttpGet(url));
        
        int statusCode = response.getStatusLine().getStatusCode();
        String reasonPhrase = response.getStatusLine().getReasonPhrase();
        String content = EntityUtils.toString(response.getEntity());
        
        if (statusCode != 200) {
            log.fatal("HTTP Status Code: {} Message: {}", statusCode, reasonPhrase);
            log.fatal("Unable to fetech: {}", url);
        }
        
        map.put("statusCode", statusCode);
        map.put("content", content);
        
    }
    catch (ClientProtocolException e) {
        log.error(e.getMessage(), e);
    }
    catch (IOException e) {
        log.error(e.getMessage(), e);
    }

    return map;
}

`

MMirabito avatar Dec 25 '19 17:12 MMirabito

Dear all, sorry to continue to pester you all but I am still curios if anyone using WAFFLE has had to deal with the use case I describe above.

In summary once a user using a browser has negotiated the authentication via Tomcat using the WAFFLE filter I would like to pass the valid authentication/token/information that just occurred to another call within the same servlet using an HTTP Client request.

Any guidance, suggestion or comments would be greatly appreciated.

Thanks max

MMirabito avatar Jan 02 '20 11:01 MMirabito

Please also post this on the waffle google group. You will likely get responses over there. Off hand I dont know if this is possible. Your write up is excellent though and worth getting the larger user group to weigh in.

Get Outlook for Androidhttps://aka.ms/ghei36


From: Max Mirabito [email protected] Sent: Thursday, January 2, 2020 6:51:39 AM To: Waffle/waffle [email protected] Cc: Subscribed [email protected] Subject: Re: [Waffle/waffle] Using WAFFLE from a servlet and subsequently passing the authorization to another resource using HTTPClient from a servlet (#846)

Dear all, sorry to continue to pester you all but I am still curios if anyone using WAFFLE has had to deal with the use case I describe above.

In summary once a user using a browser has negotiated the authentication via Tomcat using the WAFFLE filter I would like to pass the valid authentication/token/information that just occurred to another call within the same servlet using an HTTP Client request.

Any guidance, suggestion or comments would be greatly appreciated.

Thanks max

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHubhttps://github.com/Waffle/waffle/issues/846?email_source=notifications&email_token=AAHODIZUSDHATSKKPJOIB43Q3XIMXA5CNFSM4J7F5U22YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEH6FWXY#issuecomment-570186591, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AAHODIZWE2YAKTI6OS57YXDQ3XIMXANCNFSM4J7F5U2Q.

hazendaz avatar Jan 02 '20 19:01 hazendaz

Sorry for such a wordy reply with now real answer

Here's something that can hopefully help you get closer to accessing your Additional resource; it looks like you need to use (Negotiate) Kerberos with constrained delegation because you are attempting a double hop where the Servlet is now trying to access the Additional resource as the user who called it i.e. the Web Browser

Browser (User1) -> Servlet -> HTTPClient (as User1) -> Next Server (Additional resource) | |_call to "Next Server" === Use Kerberos Constrained Delegation |_access_local_file_as_user User1 === Use Impersonation

NTLM and Kerberos (Negotiate) do not allow you to use another process's Token without "pre authorization" Impersonation can be used to access a resource on the Servlet Server as the Browser User (User1) Delegation is when the Thread that is executing the Code on the Server is an SPN that can use the Token of the Browser User (User1) to access a remote resource "Next Server"

If you want the servlet to act as the user who has called the Servlet then you will require an SPN and a service account with delegation NTLM and Negotiate (Kerberos) are Connected protocols (keep-alive) "... the protocol actually authenticates the TCP connection..." I think that you will have to use the Kerberos Token during the SPNEGO flow the "Next Server" is referred to as a second hop and in non java environments like PowerShell, you would have to enable CredSSP or use Kerberos with Constrained Delegation (KCD)

Even though this author has solved the problem using SPNEGO, if should be possible to use some of the helper classes to help you and your HTTP Client https://github.com/tellisnz/collared-kerberos from here ( https://stackoverflow.com/questions/39743700/java-spnego-authentication-kerberos-constrained-delegation-kcd-to-backend-se ) https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/bde93b0e-f3c9-4ddf-9f44-e1453be7af5a

  1. the service account must be granted delegation; your domain admin will do this using GPOs and Property Preferences
  2. your DNS domain must be a trusted domain or an intranet domain to ensure that the Browser User's real token is sent to the Servlet
  3. you will require a service account to run the Tomcat Service and have an SPN for the user and the user will require e.g. mywindomain\tcserver add the user as SPN HTTP//myapp.intranet.local:5884 the PORT is part of the SPN
  4. your HTTPClient would now be able to call the "Next Server" as User1

constraints and infrastructure resources. a keep-alive connection is required from Browser a thread is used from Browser to Next Server because the Delegation is all authenticated at TCP level which can impact connections and performance of your Servlet and the hosting server

https://stackoverflow.com/questions/39743700/java-spnego-authentication-kerberos-constrained-delegation-kcd-to-backend-se

https://it.toolbox.com/question/thread-gets-blocked-while-executing-httpclientexecute-method-021910

https://docs.oracle.com/javase/7/docs/technotes/guides/net/http-auth.html https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/enable-computer-and-user-accounts-to-be-trusted-for-delegation

https://blogs.msdn.microsoft.com/connector_space/2018/06/07/service-accounts-spns-and-kerberos-delegation-configurations-for-mim-service-and-portal-installation/

https://docs.microsoft.com/en-us/windows-server/security/kerberos/kerberos-constrained-delegation-overview https://tools.ietf.org/html/rfc1704

https://docs.oracle.com/javase/10/security/single-sign-using-kerberos-java1.htm#JSSEC-GUID-38C14739-0A86-46EA-B0E9-44A7CD6AA9E4

On Thu, 2 Jan 2020 at 11:51, Max Mirabito [email protected] wrote:

Dear all, sorry to continue to pester you all but I am still curios if anyone using WAFFLE has had to deal with the use case I describe above.

In summary once a user using a browser has negotiated the authentication via Tomcat using the WAFFLE filter I would like to pass the valid authentication/token/information that just occurred to another call within the same servlet using an HTTP Client request.

Any guidance, suggestion or comments would be greatly appreciated.

Thanks max

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/Waffle/waffle/issues/846?email_source=notifications&email_token=AAQAQNCIPU2KUY3PG4P4H3LQ3XIMXA5CNFSM4J7F5U22YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEH6FWXY#issuecomment-570186591, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAQAQNGKLK3F4CBCUK5FT6LQ3XIMXANCNFSM4J7F5U2Q .

pedroneil avatar Jan 02 '20 22:01 pedroneil

Please also post this on the waffle google group. You will likely get responses over there. Off hand I dont know if this is possible. Your write up is excellent though and worth getting the larger user group to weigh in.

@hazendaz thanks for the suggestion I will do that as well

max

MMirabito avatar Jan 03 '20 13:01 MMirabito

@pedroneil thanks for your reply and no worries for the length. You confirmed my suspicion that it's not as simple as I would like it - I was hoping for a quick - ready made solution and declare victory.

I will read through your post and see were it takes me.

Thanks again,

max

MMirabito avatar Jan 03 '20 13:01 MMirabito

@MMirabito I noticed our main github here did not show the google group directly as many other projects do. I've added the email there. For reference, it is [email protected]. I think that is listed either on the readme in within the various docs folder.

I would like to hear final thoughts back on what you accomplish. This entire issue seems like a great candidate to be converted to documentation regardless of success or not. At least that will assist others on this same journey.

hazendaz avatar Jan 05 '20 22:01 hazendaz

I'd never realised that there's this group existed, thanks for https://groups.google.com/forum/m/#!forum/waffle-users

On Sun, 5 Jan 2020, 22:29 Jeremy Landis, [email protected] wrote:

@MMirabito https://github.com/MMirabito I noticed our main github here https://github.com/waffle did not show the google group directly as many other projects do. I've added the email there. For reference, it is [email protected]. I think that is listed either on the readme in within the various docs folder.

I would like to hear final thoughts back on what you accomplish. This entire issue seems like a great candidate to be converted to documentation regardless of success or not. At least that will assist others on this same journey.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Waffle/waffle/issues/846?email_source=notifications&email_token=AAQAQNGIJBIZ2VGVNUREU43Q4JNM7A5CNFSM4J7F5U22YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEIEBGAQ#issuecomment-570954498, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAQAQNEL5W6AOG2BCMQDVXLQ4JNM7ANCNFSM4J7F5U2Q .

pedroneil avatar Jan 06 '20 08:01 pedroneil

@hazendaz thanks for the google group link. I just posted the same question her is the link for reference https://groups.google.com/forum/#!topic/waffle-users/MHANogskG48

Thanks, max

MMirabito avatar Jan 07 '20 12:01 MMirabito