Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do authentication in UIWebView properly?

Tags:

I would like to support HTTP Basic Authentication in my UIWebView.

At the moment, I am canceling requests in

webView:shouldStartLoadWithRequest:navigationType: then handle them in my own NSURLConnectionDelegate to check for and provide credentials if needed. I then use loadData:MIMEType:textEncodingName:baseURL: to present HTML in the web view. That works fine for any URLs that are passed to the delegate.

My problem is that the delegate is never called for embedded elements, like images, JavaScript or CSS files. So if I have an HTML page which references an image which is protected with basic authentication, that image cannot be loaded properly. Additionally, webView:didFinishLoad: is never called, because the web view could not fully load the page.

I have checked that case with Terra, a third-party browser available on the App Store, and it can fully cope with that situation. I think it would be possible to solve this by providing my own NSURLProtocol, but that seems too complicated. What am I missing?

like image 539
NeoNacho Avatar asked Jan 25 '12 08:01

NeoNacho


People also ask

How do I authenticate a URL?

We can do HTTP basic authentication URL with @ in password. We have to pass the credentials appended with the URL. The username and password must be added with the format − https://username:password@URL.

What is authentication in Swift?

Authentication is the process of identifying a user. Authorization is the process of verifying which resources the user has access to. As the name suggests, basic authentication is a simple, straightforward method to authenticate a user over HTTP(S).

What does UIWebView mean?

Android is powered by Chrome. Mobile Safari UIWebView. The UIWebView is different from the ordinary Safari browser, as it is not a stand-alone browser, but merely browser functionality that is embedded in a third party app that allows the app to display content from the web.


1 Answers

Try to use sharedCredentialStorage for all domains you need to authenticate.

Here is working sample for UIWebView it was tested against Windows IIS having only BasicAuthentication enabled

This is how to add your site credentials:

NSString* login = @"MYDOMAIN\\myname"; NSURLCredential *credential = [NSURLCredential credentialWithUser:login                                                          password:@"mypassword"                                                       persistence:NSURLCredentialPersistenceForSession];  NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]                                          initWithHost:@"myhost"                                                  port:80                                              protocol:@"http"                                                 realm:@"myhost" // check your web site settigns or log messages of didReceiveAuthenticationChallenge                                  authenticationMethod:NSURLAuthenticationMethodDefault];  [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential                                                     forProtectionSpace:protectionSpace]; [protectionSpace release]; 

Edit: same code in Swift 4

let login = "MYDOMAIN\\myname" let credential = URLCredential(user:login, password:"mypassword", persistence:.forSession) let protectionSpace = URLProtectionSpace(host:"myhost", port:80, protocol:"http", realm:"myhost", authenticationMethod:NSURLAuthenticationMethodDefault) URLCredentialStorage.shared.setDefaultCredential(credential, for:protectionSpace) 

Your webView is supposed to work now, if it does not work use next code to debug, especially check log messages of didReceiveAuthenticationChallenge.

    #import "TheSplitAppDelegate.h"     #import "RootViewController.h"      @implementation TheSplitAppDelegate      @synthesize window = _window;     @synthesize splitViewController = _splitViewController;     @synthesize rootViewController = _rootViewController;     @synthesize detailViewController = _detailViewController;      - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions     {         // Override point for customization after application launch.         // Add the split view controller's view to the window and display.         self.window.rootViewController = self.splitViewController;         [self.window makeKeyAndVisible];          NSLog(@"CONNECTION: Add credentials");          NSString* login = @"MYDOMAIN\\myname";         NSURLCredential *credential = [NSURLCredential credentialWithUser:login                                                                  password:@"mypassword"                                                               persistence:NSURLCredentialPersistenceForSession];          NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]                                                  initWithHost:@"myhost"                                                  port:80                                                  protocol:@"http"                                                  realm:@"myhost" // check your web site settigns or log messages of didReceiveAuthenticationChallenge                                                  authenticationMethod:NSURLAuthenticationMethodDefault];           [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace];         [protectionSpace release];              NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://myhost/index.html"]                                                                cachePolicy:NSURLRequestReloadIgnoringCacheData                                                            timeoutInterval:12                                         ];          NSLog(@"CONNECTION: Run request");         [[NSURLConnection alloc] initWithRequest:request delegate:self];          return YES;     }      - (void)applicationWillResignActive:(UIApplication *)application     {      }      - (void)applicationDidEnterBackground:(UIApplication *)application     {      }      - (void)applicationWillEnterForeground:(UIApplication *)application     {      }      - (void)applicationDidBecomeActive:(UIApplication *)application     {      }      - (void)applicationWillTerminate:(UIApplication *)application     {      }      - (void)dealloc     {         [_window release];         [_splitViewController release];         [_rootViewController release];         [_detailViewController release];         [super dealloc];     }      - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;     {         NSLog(@"CONNECTION: got auth challange");         NSString* message = [NSString stringWithFormat:@"CONNECTION: cred cout = %i", [[[NSURLCredentialStorage sharedCredentialStorage] allCredentials] count]];         NSLog(message);         NSLog([connection description]);          NSLog([NSString stringWithFormat:@"CONNECTION: host = %@", [[challenge protectionSpace] host]]);         NSLog([NSString stringWithFormat:@"CONNECTION: port = %i", [[challenge protectionSpace] port]]);         NSLog([NSString stringWithFormat:@"CONNECTION: protocol = %@", [[challenge protectionSpace] protocol]]);         NSLog([NSString stringWithFormat:@"CONNECTION: realm = %@", [[challenge protectionSpace] realm]]);         NSLog([NSString stringWithFormat:@"CONNECTION: authenticationMethod = %@", [[challenge protectionSpace] authenticationMethod]]);     }      - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{         // release the connection, and the data object         [connection release];          // inform the user         NSLog(@"CONNECTION: failed! Error - %@ %@",               [error localizedDescription],               [[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);     }       - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;     {         NSLog(@"CONNECTION: received response via nsurlconnection");     }      - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;     {         NSLog(@"CONNECTION: USE!");         return YES;     }       @end 

The final solution for WebView authentication was based on custom protocol implementation. All protocols registered as a stack, so if you redefine HTTP protocol it would intercept all requests coming from webView, so you have to check attributes assotiated with incoming request and repack it into new request and send it again via your own connection. Since you are in stack, your request immidiatly comes to you again and you have to ignore it. So it goes down protocol stack to real HTTP protocol implementation, since your request is not athenticated you'll get authenticaiton request. And after authenticaiton you'll get a real response from server, so you repack response and reply to original request received from webView and that's it.

Don;t try to create new requests or responses bodies, you have to just resend them. The final code would be aproximetly 30-40 lines of code and it is quite simple, but requires a lot of debuging and tetsing.

Unfortunatlly I cannot provide code here, since I am assigned to different project already, I just wanted to say that my post is wrong way, it stucks when user changes password.

like image 79
Basic Primitives Support Avatar answered Sep 18 '22 17:09

Basic Primitives Support