Search code examples
c#proxyxamarin.ioswebrequest

MonoTouch auto proxy network credentials not working


Trying to load some data off my server (xml data). I normally just use HttpWebRequest as I can set the utomaticDecompression to DecompressionMethods.GZip | DecompressionMethods.Deflate in order to use gzip compression of my data automatically.

Recently a client has emailed me saying that it does not work from within his school. Sent out another copy of the app to him via TestFlightApp to get more debugging info from him and the app responds with a 407 auth error. Since then I have set up a proxy on my computer which I redirect my iPhone traffic through to test on.

My original setup was like so,

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_url);
request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
request.Proxy = null;
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
WebResponse response = request.GetResponse();

and then so on from there.

I'd set Proxy to null because in most cases the user would not be behind a proxy and this cut off 1-2 seconds of proxy resolving. There is code later to catch this and then re-create the HttpWebRequest but by not nulling the proxy, which I thought would be default proxy. Apparently not so...

I tried to then get the default proxy settings of the device.

CFProxySettings proxy0 = CFNetwork.GetSystemProxySettings();
IWebProxy proxy1 = WebRequest.GetSystemWebProxy();
IWebProxy proxy2 = HttpWebRequest.DefaultWebProxy;
IWebProxy proxy3 = HttpWebRequest.GetSystemWebProxy();
IWebProxy proxy4 = CFNetwork.GetDefaultProxy();

proxy1 - proxy4 have null for the credentials. proxy0 contains proxy information which I need such as proxy url, port, username, but no password...

I manually made a WebProxy with my credentials

WebProxy webProxy = new WebProxy(proxy0.HTTPProxy, proxy0.HTTPPort);
webProxy.Credentials = new NetworkCredential("user", "pass");
request.Proxy = webProxy;

and this works. Falling short of requesting username and password for the proxy on a 407 error I am not sure what to do... WebClient downloads do not work, but UIWebView and MKMapView does auto-auth itself.

Any ideas what I am missing?

EDIT:

Found this, https://bugzilla.xamarin.com/show_bug.cgi?id=1081


Solution

  • I had a look at this today, doing a lot of debugging and browsing Apple's Documentation - and I'm afraid, I don't have a positive answer for you - unless you consider "impossible" as a positive answer ...

    The Problem in General

    According to Apple's Documentation, CFProxySupport does not give you access to the stored proxy credentials.

    There is an - undocumented - way of getting the proxy username (the returned dictionary uses strings as keys, one of these keys is called "HTTPUser" or "HTTPProxyUsername", but I couldn't find any string constant for it), but not the password.

    On Mac OS X

    There is actually a solution on Mac OS X: when I ran a simple NSURLConnection based example, it asked me for permission to access the keychain, so I opened the Keychain Utility and found the proxy credentials in the login keychain. I also wrote a simple Objective C function to retrieve it, but you need to know the proxy user name (but I haven't tried using NULL, maybe that works).

    I also couldn't find any way of checking whether authentication is required at all, except by checking whether that username is NULL. Unfortunately, you need keychain access privileges to check whether some item exists, so we need to make sure that authentication is actually requierd. One way of doing that would be from inside HttpWebConnection after it already received the HTTP 407 (and of these also contain the desired username).

    On iOS

    From what I've read so far on the internet, iOS does not let you access the system keychain - I'm not even sure whether that exists at all. The CFProxySupport also doesn't return any credentials.

    Fwiw., you also don't ever need to know the proxy password unless you want to do HTTP access without using the iOS APIs for it. So there's a decent chance that there's just no way of retrieving it.

    As a solution, you could either use the iOS APIs such as NSURLConnection or WebClient with the CFNetwork handler.