38

I am trying to access a secure website through UIWebView. When I access it through safari, i get an authentication challenge but the same does not appear in my UIWebView in the application. How can I make it appear?

Any pointers, sample code or links will be very helpful. Thanks a lot.

MPelletier
  • 16,256
  • 15
  • 86
  • 137
UVT
  • 521
  • 1
  • 4
  • 9

5 Answers5

38

It's actually super easy... I'm sure you can just show a UIAlertView when the auth challenge delegate is shown (or prior to loading the URL, if you know for sure that the URL you're hitting will prompt for auth login info). Anyways, the trick is to create your own NSURLConnection and I do some logic to save whether the auth delegate has been used.

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
{
    NSLog(@"Did start loading: %@ auth:%d", [[request URL] absoluteString], _authed);

    if (!_authed) {
        _authed = NO;
        /* pretty sure i'm leaking here, leave me alone... i just happen to leak sometimes */
        [[NSURLConnection alloc] initWithRequest:request delegate:self];
        return NO;
    }

    return YES;
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
{
    NSLog(@"got auth challange");

    if ([challenge previousFailureCount] == 0) {
        _authed = YES;
        /* SET YOUR credentials, i'm just hard coding them in, tweak as necessary */
        [[challenge sender] useCredential:[NSURLCredential credentialWithUser:@"username" password:@"password" persistence:NSURLCredentialPersistencePermanent] forAuthenticationChallenge:challenge];
    } else {
        [[challenge sender] cancelAuthenticationChallenge:challenge];
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
{
    NSLog(@"received response via nsurlconnection");

    /** THIS IS WHERE YOU SET MAKE THE NEW REQUEST TO UIWebView, which will use the new saved auth info **/

    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:]];

    [_webView loadRequest:urlRequest];
}

- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
{
    return NO;
}
Max MacLeod
  • 26,115
  • 13
  • 104
  • 132
Sahil
  • 1,268
  • 12
  • 19
  • 2
    Does not work with POSTs. Even a simple modification of this still does not work with POSTs. – Wayne Hartman Oct 16 '10 at 01:30
  • 2
    This does not work for any non-authenticated requests (_authed always == NO and an infinite loop is triggered). Even if you fix that problem, this still doesn't work because shouldStartLoadWithRequest is fired multiple times per web page which ultimately results in the last url request that the web view loads being the only content that is displayed in the web view. – Zach Apr 22 '11 at 05:02
  • 1
    add '_authed = YES' to didReceiveResponse. This prevents infinite loop when server did not send challenge request for some reason. – Seunghoon Oct 11 '11 at 08:43
  • The answer has a couple issues, but it gives the basic concept to solve the problem for authenticated requests. – DBD Aug 22 '12 at 19:54
  • 2
    It seems that when the connection breaks, the auth data is lost while `_authed` is still `YES`, and there is no chance to auth again. – Wei Liu Feb 24 '13 at 16:47
  • Is this code worked for you. Because i am having same problem now. Csn you please some links or more clear. – Ganesh G May 24 '13 at 05:09
  • +1 @Sahil, it's working. But i need to set credentials dynamically. Is it possible? – Ganesh G May 24 '13 at 05:18
  • @G.Ganesh Does this work efficiently? Seems like it would cause two round trips to the server when accessing a non-protected resource (since when you get a response, you immediately call loadRequest again). – Travis Oct 22 '13 at 21:07
  • There's gotta be a better way...The line that leaks memory is screwing everything up when I try to use NSURLConnection on another tab in a tab bar controller. – mafiOSo Feb 27 '14 at 00:53
  • If you use WKWebView, handle the challenge in WKNavigationDelegate `func webView(webView: WKWebView, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void)` – ethanhuang13 Feb 24 '16 at 08:08
6

You can also provide your credentials in the url. Just add username and password between 'http://' and 'page url'.

NSString *urlString = @"http://username:password@domain.com/home";
[webview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]];
Mickael West
  • 341
  • 4
  • 6
  • 1
    Someone on the same network could see the request in clear. => Using https with an SSL certificate would solve this issue. You can check that with a sniffer such as Wireshark. – Mickael West Sep 12 '14 at 19:44
4

as you know, UIWebView does not provide opportunities to communicate with the server. I solved this problem this way: in the delegate method shouldStartLoadWithRequest of UIWebView I initiating another connection with NSURLConnection, and already in the method of the delegate NSURLConnection didReceiveAuthenticationChallenge processed the сhallenge from the server. Аnd in the method didReceiveResponse (if the challenge came) then again in the same UIWebView load the same URL (challenge has already been processed:). Do not forget to cancel connection in didReceiveResponse, otherwise it will double the traffic.

alexandrmoroz
  • 337
  • 4
  • 12
  • 2
    If you have already received the response, how does canceling the connection prevent doubling the traffic? – Travis Oct 22 '13 at 21:06
1

if you are experiencing symptoms of what Zach described in the comments of Sahil's answer:

as y5h said as well, add `_authed = YES' to the didReceiveResponse method which will stop the infinite looping. even if the auth wasnt successful you need to treat it as if it was authed so it will try continuing loading the page if no authentication is required, if authentication was really required, then it will just fail like normal.

for the second symptom where the shouldStartLoadWithRequest: fires multiple times (due to embedded content on the webpage) and it will just show the last thing that loaded and not the whole web page, do this:

in the shouldStartLoadWithRequest: method, add this to the top

if(webview.loading){ //if url requests come through while its loading, its probably embedded content
    return YES;
}

edit: this above method has issues if the page fully loads, and then loads more embedded content afterwards, breaks with facebook which is the only case ive seen so far

this will let urls through while the website is trying to load. im not sure if its safe to assume that every url after the initial request is embedded content, but for my purposes it seemed to work, so maybe it will for you as well.

also, use

- (void) connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge

because

connection:canAuthenticateAgainstProtectionSpace:
connection:didReciveAuthenticationChallenge:
connection:didCancelAuthenticationChallenge:

are depricated, and for me, you could not authenticate with https websites using them

Fonix
  • 11,447
  • 3
  • 45
  • 74
0

I would like to suggest another answer using the following pod: https://github.com/jivesoftware/JiveAuthenticatingHTTPProtocol

It contains an example showing how to use a UIAlertView to ask the user for the password, and could easily be adapted to return a saved password from a local DB for instance.

Disclaimer: I am in no way affiliated with Jive nor have I ever been, this is just a suggestion of a tool that helped me after struggling for days on this.

dvkch
  • 1,079
  • 1
  • 12
  • 20