How can I get UIWebView to open the Facebook login page in response to an OAuth request on iOS 5 and iOS 6?

We have:
(1) Facebook API Web Application with Facebook OAuth (“FB Web Application”)
(2) UIWebView based browser on iPad ("Browser")

Our goal is to open the Facebook login page in order to enter the FB (1) web application in a browser based on UIWebView (2) on the iPad.

There is a somewhat similar problem here:
http://facebook.stackoverflow.com/questions/11337285/no-longer-able-to-login-to-ios-app-via-oauth-the-page-requested-was-not-found

However, the question about this arises after the user enters the username and password into the Facebook form. Our problem is that we cannot get the Facebook login form displayed first. Changing the type of application from "Web" to "Home / Desktop", as suggested in this matter, did not help.

Steps:
1. Open our web page (simple HTML page) using this UIWebView browser
2. Press the start button "FB web app" on this page
3. OnClick JavaScript is trying to initiate OAuth, which should open the Facebook login screen to enter the FB web application.

Current Result (Release):
On iOS 5. + and iOS 6. + devices
- Our web page remains unchanged
- Facebook login page is NOT displayed (our web page is still displayed)
On iOS 4.3 (works as expected):
- the Facebook login page opens in the same UIWebView browser object (replaces our web page)

Expected Result:
- A Facebook login page is displayed, and the user can enter the Facebook login and password

- Works on iOS 5. + and iOS 6. + if running in Safari on iPad. The Facebook login page opens on a separate tab (on the contrary, there are no separate tabs in UIWebView )

Question: How can I get a UIWebView to open the Facebook login page in response to an OAuth request on iOS 5+ and iOS 6+?

Additional technical details:

We write different NSURLRequest fields from

 -(BOOL)webView(UIWebView*)webView shouldStartLoadWithRequest(NSURLREquest*)request navigationType:… 

And we notice some difference in the logs for the “right” and “wrong” behavior. Here's how the execution processes change:

Firstly, I press the “FB Web App” launch button to initiate OAuth, then some cases go

iOS 4.3, "correct"
request at www.facebook.com/dialog/oauth? ... request at fbwebapp.com
request m.facebook.com/login.php? ....
- A personal facebook login will appear

iOS 5.0, "correct1"
request at www.facebook.com/dialog/oauth? ... request at fbwebapp.com
request m.facebook.com/login.php? ...

Then it can be - a lot of m.facebook.com/login.php?...with next ... in the parameters
followed by sqlite error
- Now I see that the page “Sorry, something went wrong” from facebook (this is the first time I come across it)

iOS 6.0 "wrong2"
request at www.facebook.com/dialog/oauth? ... request at fbwebapp.com

- (void) webView: (UIWebView *) webView error didFailLoadWithError: (NSError *) is called with error code -999

You can see that this definitely depends on the version of iOS. But a common case is that the error occurs at the stage of obtaining m.facebook.com/login.php .. URL. But that’s all we can find.

All day we hit our head against this wall in search of solutions. Hopelessly.
Can you help us open the Facebook login page in UIWebView in response to OAuth?

+6
source share
5 answers

just use: this code

 if (![FBSDKAccessToken currentAccessToken]) { FBSDKLoginManager *manager = [[FBSDKLoginManager alloc]init]; manager.loginBehavior = FBSDKLoginBehaviorWeb; [manager logInWithReadPermissions:@[@"public_profile", @"email", @"user_friends"] handler: ^(FBSDKLoginManagerLoginResult *result, NSError *error) { NSLog(@"result.token: %@",result.token.tokenString); NSLog(@"%@",result.token.userID); NSLog(@"%hhd",result.isCancelled); }]; } 

// here manager.loginBehavior = FBSDKLoginBehaviorWeb; - all you need to open facebook in UIWebview

+4
source

Did this!

It's kind of a hack, but js facebook sdk login on UiWebView on iOS 6 is finally working.

How can I do that? This is a pure JS + Facebook JS SDK + UIWebView Delegate solution for handling functions.

JS is the first step)

the login button (for connecting via facebook) calls this example function, which brings up the Face JS login / logout dialogs:

 function loginWithFacebookClick(){ FB.login(function(response){ //normal browsers callback }); } 

JS is the second step)

add an authResponseChange listener every time a user loads a web page (after FB.init ()) to catch the state of the connected user:

 FB.Event.subscribe('auth.authResponse.Change', function(response){ //UIWebView login 'callback' handler var auth = response.authResponse; if(auth.status == 'connected'){ //user is connected with facebook! just log him at your webapp } }); 

And with the UIWebView application delegation function, you can process the answers in facebook oauth

Goal C - Third Step)

 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSString *url = [[request URL] absoluteString]; //when user status == connected (has a access_token at facebook oauth response) if([url hasPrefix:@"https://m.facebook.com/dialog/oauth"] && [url rangeOfString:@"access_token="].location != NSNotFound) { [self backToLastPage]; return NO; } return YES; } - (void)webViewDidFinishLoad:(UIWebView *)webView { NSString *url = [[webView.request URL] absoluteString]; if([url hasPrefix:@"https://m.facebook.com/dialog/oauth"]) { NSString *bodyHTML = [webView stringByEvaluatingJavaScriptFromString:@"document.body.innerHTML"]; //Facebook oauth response dead end: is a blank body and a head with a script that does //nothing. But if you got back to your last page, the handler of authResponseChange //will catch a connected status if user did his login and auth app if([bodyHTML isEqualToString:@""]) { [self backToLastPage]; } } } 

Thus, when the user redirects to the last loaded page, the second step will perform the action of the user exit in the facebook login dialogs.

If I'm too quick with this answer, ask me! Hope this helps.

+3
source

In case someone searches the web, here is what worked for me:

 -(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)inType { if ([request.URL.absoluteString containsString:@"m.facebook.com"]) { if ([request.URL.absoluteString rangeOfString:@"back"].location == 0) { [self.popUp removeFromSuperview]; self.popUp = nil; return NO; } if (self.popUp) { return YES; } UIWebView *wv = [self popUpWebView]; [wv loadRequest:request]; return NO; } return YES; } - (UIWebView *) popUpWebView { toolbar height UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, (float)self.view.bounds.size.width, (float)self.view.bounds.size.height)]; webView.scalesPageToFit = YES; webView.delegate = self; // Add to windows array and make active window self.popUp = webView; [self.view addSubview:webView]; return webView; } - (void)webViewDidFinishLoad:(UIWebView *)webView { if (self.popUp) { NSError *error = nil; NSString *jsFromFile = @"window.close=function(){window.location.assign('back://' + window.location);};"; __unused NSString *jsOverrides = [webView stringByEvaluatingJavaScriptFromString:jsFromFile]; JSContext *openerContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; JSContext *popupContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; popupContext[@"window"][@"opener"] = openerContext[@"window"]; } //this is the secret sauce if (webView == self.popUp && [webView.request.URL.absoluteString containsString:@"m.facebook.com"] && [[webView stringByEvaluatingJavaScriptFromString:@"document.body.innerHTML"] isEqualToString:@""]) { [webView stringByEvaluatingJavaScriptFromString:@"eval(document.getElementsByTagName('script')[0].text)"]; } } 

I hooked a bunch of this implementation .

Depending on your web implementation, there will probably be one additional step. The Facebook script actually does window.close() , then a window.open() , then a window.close() . This caused problems for me, because on the web page after completing this login, my window (i.e., for the web browser to which I want the user to log in) received a window.close() call coming from the SDK for Facebook I guess this is because in the Facebook SDK, it is expected that calling window.open() will open a new window that it will close.

Since we have not redefined the functionality of window.open() , calling window.open() do nothing and the Facebook SDK will try to close your window. This can cause all problems, but for me, since I am using Parse, window.localStorage was set to null , so I got all kinds of errors.

If something like this happens to you, you have two options:

  • If you have control over the web code, and yours down for a little hack, drop this into window.close=function(){}
  • If you do not have control over the web code, you can either add an override to window.close for the main web browser, as we did for the popUp web application, or override the window.open function to open another popUp (which is described in more details here )
+1
source

Use the FBDialog class to prompt the user to log in. This uses web browsing inside the application, so upon successful login, the user will log in inside any UIWebView:

 NSString *kRedirectURL = @"fbconnect://success"; NSString *kSDK = @"ios" ; NSString *kLogin = @"oauth"; NSString *kDialogBaseURL = @"https://m.facebook.com/dialog/"; NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys: AE_FACEBOOK_APPID, @"client_id", @"user_agent", @"type", kRedirectURL, @"redirect_uri", @"touch", @"display", kSDK, @"sdk", nil]; NSString *loginDialogURL = [kDialogBaseURL stringByAppendingString:kLogin]; FBLoginDialog* loginDialog = [[FBLoginDialog alloc] initWithURL:loginDialogURL loginParams:params delegate:self]; [loginDialog show]; 

Then make your class stick to the FBDialogDelegate protocol and add this function to your class:

 -(void)fbDialogLogin: (NSString *)token expirationDate:(NSDate *)expirationDate{ // Store the token and expiration date into the FB SDK self.facebook.accessToken = token; self.facebook.expirationDate = expirationDate; // Then persist these so the SDK picks them up on next load NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:self.facebook.accessToken forKey:ACCESS_TOKEN_KEY]; [defaults setObject:self.facebook.expirationDate forKey:EXPIRATION_DATE_KEY]; [defaults synchronize]; } 

NTN!

0
source

How to register on facebook in UIWebView.

Objective-c

Use taylorstine's answer . He saved me a day. Thanks taylorstine

But I use Swift 3. so I just converted the code from taylorstine's answer.

Swift 3.

 func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool { if let _ = request.url?.absoluteString.range(of: "m.facebook.com" ) { if let _ = request.url?.absoluteString.range(of: "back"){ self.popUp?.removeFromSuperview() self.popUp = nil return false } if let _ = self.popUp { return true } let wv = popUpWebView() wv.loadRequest(request) return false } return true } func popUpWebView() -> UIWebView { let webView = UIWebView(frame: self.view.frame) webView.delegate = self self.popUp = webView self.view.addSubview(webView) return webView } func webViewDidFinishLoad(_ webView: UIWebView) { if let _ = self.popUp { let jsFromFile = "window.close=function(){window.location.assign('back://' + window.location);};" let _ = webView.stringByEvaluatingJavaScript(from: jsFromFile) let openerContext = self.webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as! JSContext let popupContext = webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as! JSContext popupContext.setObject("opener", forKeyedSubscript: "window" as (NSCopying & NSObjectProtocol)!) popupContext.setObject(openerContext.objectForKeyedSubscript("window"), forKeyedSubscript: "opener" as (NSCopying & NSObjectProtocol)!) } if webView == self.popUp && (webView.request?.url?.absoluteString.range(of:"m.facebook.com") != nil) && webView.stringByEvaluatingJavaScript(from: "document.body.innerHTML") == "" { webView.stringByEvaluatingJavaScript(from: "eval(document.getElementsByTagName('script')[0].text)") } } 
0
source

All Articles