forcedotcom / wsc

Other
271 stars 222 forks source link

Proxy returns "HTTP/1.1 407 Proxy Auth Required" #87

Open varad opened 9 years ago

varad commented 9 years ago

Hi, we use following code to connect to Salesforce using Partner API Java library (com.force.api.force-partner-api), and we tested tunneling through local proxy with proxy authentication (username and password).

I am using Fiddler Web Debugger to emulate proxy, having the proxy authentication credentials set to 1:1 (username: 1, password: 1).

When the following code runs it throws exception you can see below when trying to create Connection.

ConnectorConfig partnerConfig = new ConnectorConfig();
partnerConfig.setUsername(userName);
partnerConfig.setPassword(password);
partnerConfig.setAuthEndpoint("https://login.salesforce.com/services/Soap/u/32.0");
partnerConfig.setProxy("localhost", 8888);
partnerConfig.setProxyUsername("1");
partnerConfig.setProxyPassword("1");

partner = new PartnerConnection(partnerConfig);

Exception:

 com.sforce.ws.ConnectionException: Failed to send request to https://login.salesforce.com/services/Soap/u/32.0
at com.sforce.ws.transport.SoapConnection.send(SoapConnection.java:121)
at com.sforce.soap.partner.PartnerConnection.login(PartnerConnection.java:1349)
at com.sforce.soap.partner.PartnerConnection.<init>(PartnerConnection.java:405)
...
 Caused by: java.io.IOException: Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407 Proxy Auth Required"
at sun.net.www.protocol.http.HttpURLConnection.doTunneling(HttpURLConnection.java:2083)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:183)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1281)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1256)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:250)
at com.sforce.ws.transport.JdkHttpTransport.connectRaw(JdkHttpTransport.java:136)
at com.sforce.ws.transport.JdkHttpTransport.connectLocal(JdkHttpTransport.java:100)
at com.sforce.ws.transport.JdkHttpTransport.connectLocal(JdkHttpTransport.java:95)
at com.sforce.ws.transport.JdkHttpTransport.connect(JdkHttpTransport.java:91)
at com.sforce.ws.transport.SoapConnection.send(SoapConnection.java:95)
... 6 more

Note that the proxy authentication works otherwise, ie. when browsing the web it asks for credentials. Also when using just the proxy without authentication it works too. Only the proxy authentication doesn't work.

When I inspect the request using Fiddler I can see that the request doesn't have any auth headers as it should:

CONNECT login.salesforce.com:443 HTTP/1.1
User-Agent: Java/1.8.0_25
Host: login.salesforce.com
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Proxy-Connection: keep-alive

When I debug through the Partner API code I can see that it correctly sets the headers including the authentication headers (with value: Basic MTox, which is Base64 for "1:1"), in the class JdkHttpTransport, line 171:

    if (config.getProxyUsername() != null) {
        String token = config.getProxyUsername() + ":" + config.getProxyPassword();
        String auth = "Basic " + new String(Base64.encode(token.getBytes()));
        connection.addRequestProperty("Proxy-Authorization", auth);
        connection.addRequestProperty("Https-Proxy-Authorization", auth);
    }

However, magically the headers get lost and the request is send without them..

Does anyone know a solution to this? Thank you.

larsenfed commented 9 years ago

I am having same problem. Any news regarding this? I have read very old threads from 2012, with the same problem.

varad commented 9 years ago

Unfortunatelly I have not found a solution for the issue...

larsenfed commented 9 years ago

Thanks for the update Varad. I guess you can use the java proxy, but that means that all traffic will go through the proxy, which in same cases is not good enough. I would like to use the proxy only for SFDC connections.

varad commented 9 years ago

Yeah, you can use java proxy and that's actually what we are did. You can make use of "java.net.Authenticator" - it has a static method for setting the proxy.

Authenticator.setDefault(new ProxyAuthenticator("myUser", "myPassword"));

But that's a workaround, the library should handle this by itself. This approach works but still has issues: 1) It is a global setting - not just for Salesforce 2) We want to be able to turn on/turn off the proxy on runtime but http(s) connections are cached somewhere deep inside and we don't know how to control that.

Another approach I was thinking about was to implement com.sforce.ws.transport.Transport interface using - for example - Apache HttpClient (http://hc.apache.org/) which is much more easily configurable. But I didn't delve into it, it's just an idea you could try.

sudharsannr commented 7 years ago

@varad Which java version are you using? Adding this under Java8 still doesn't work:

Authenticator.setDefault(new Authenticator() {
            @Override
            public PasswordAuthentication getPasswordAuthentication()
            {
                return new PasswordAuthentication(proxyUsername, proxyPassword.toCharArray());
            }
        });

207 Issue created

marcinMarcin commented 7 years ago

@sudharsannr

From Java 8 Update 111 there is a change related to Basic authentication:

https://stackoverflow.com/questions/41505219/unable-to-tunnel-through-proxy-proxy-returns-http-1-1-407-via-https

rscadrde commented 6 years ago

Still happens with Java 8 Update 161:

Root Exception stack trace:
java.io.IOException: Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407 Proxy Authentication Required"
    at sun.net.www.protocol.http.HttpURLConnection.doTunneling(HttpURLConnection.java:2142)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:183)
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1334)
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1309)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:259)
    at com.sforce.ws.transport.JdkHttpTransport.connectRaw(JdkHttpTransport.java:136)
    at com.sforce.ws.transport.JdkHttpTransport.connectLocal(JdkHttpTransport.java:100)
    at com.sforce.ws.transport.JdkHttpTransport.connectLocal(JdkHttpTransport.java:95)
    at com.sforce.ws.transport.JdkHttpTransport.connect(JdkHttpTransport.java:91)
    at com.sforce.ws.transport.SoapConnection.send(SoapConnection.java:95)
    at com.sforce.soap.partner.PartnerConnection.login(PartnerConnection.java:837)
    at com.sforce.soap.partner.PartnerConnection.<init>(PartnerConnection.java:427)
    at com.sforce.soap.partner.Connector.newConnection(Connector.java:28)
    at org.mule.modules.salesforce.config.SalesforceBasicAuthConfig.establishConnection(SalesforceBasicAuthConfig.java:232)

even credentials are set correctly and working in Studios "validate Configuration" dialog.

<sfdc:config name="Salesforce__Basic_Authentication" username="${salesforce.user}" password="${salesforce.password}" proxyHost="${http.proxyHost}" proxyPort="${http.proxyPort}" proxyUsername="${http.proxyUser}" proxyPassword="${http.proxyPassword}" doc:name="Salesforce: Basic Authentication" url="${salesforce.authorization.url}"/>

Please fix.

rscadrde commented 6 years ago

The java.net.Authenticator.setDefault(Authenticator) is called correctly but java.net.Authenticator.requestPasswordAuthentication(String, InetAddress, int, String, String, String, URL, RequestorType) is never called.

Starting VM with -Djdk.http.auth.tunneling.disabledSchemes="" workarounds the problem.

rscadrde commented 6 years ago

Original cause is that com.sforce.ws.ConnectorConfig.setProxyUsername(String) is never called and org.mule.modules.salesforce.config.AbstractConfig.initConnectionConfig(ConnectorConfig) faulty sets the credentials to Authenticator than to com.sforce.ws.ConnectorConfig

Fixable via setting setProxyUsername and getProxyPassword together with setProxy in ConnectorConfig then Proxy-Authorization header will be set correctly in com.sforce.ws.transport.JdkHttpTransport.createConnection(ConnectorConfig, URL, HashMap<String, String>, boolean) 2018-03-23 09_21_07-debug - org mule modules salesforce config abstractconfig - anypoint studio - c_

KhogaEslam commented 3 years ago

can't we just use the one here, or even add it to be used using config.setTransport(HttpClientTransport.class);

Also, the same issue here https://github.com/forcedotcom/wsc/issues/207