Facebook authentication in a UIWebView does not redirect back to original page on my site asking for auth

风格不统一 提交于 2019-11-27 19:06:39
dthulke

If you are just supporting iOS 8 and up, you can use WKWebView which already implements the functionality described by @kabuko:

// Container view including the main WKWebView
var container : UIView?
var popupWebView : WKWebView?

override func viewDidLoad() {
    super.viewDidLoad()

    let prefs = WKPreferences()
    prefs.javaScriptEnabled = true
    // allow facebook to open the login popup
    prefs.javaScriptCanOpenWindowsAutomatically = true

    let config = WKWebViewConfiguration()
    config.preferences = prefs

    webView = WKWebView(frame: container.frame, configuration: config)
    webView?.UIDelegate = self
    webView?.navigationDelegate = self
}

// callback if the content of the webView wants to create a new window
func webView(webView: WKWebView, createWebViewWithConfiguration configuration: WKWebViewConfiguration, forNavigationAction navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    // create new popup webview and add it to the view hierarchy
    popupWebView = WKWebView(frame: container.frame, configuration: configuration)
    container.addSubview(popupWebView!)
    return popupWebView
}

func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
    // if the main webView loads a new page (e.g. due to succesful facebook login)
    // remove the popup
    if (popupWebView != nil) {
        popupWebView?.removeFromSuperview()
        popupWebView = nil
    }
}

I've seen something similar happen with other sites' FB logins (e.g. Groupon) if you load them in a UIWebView. If this is the same problem (which I think it is), it is due to Facebook opening up the login window in a popup as you suspected. What happens on a normal browser is that another window (popup) is opened for login, and then when the user logs in, that login window communicates back to the original window to say that it has logged in. They probably use EasyXDM or something similar. There seem to be a few layers of strategies to communicate including Flash and postMessage.

On iOS (and Android) this should mean it'll end up communicating with postMessage. If you track the URLs that go through your UIWebView you should see something like this near the end:

https://s-static.ak.fbcdn.net/connect/xd_proxy.php#<lots of stuff>&relation=opener&transport=postmessage&<lots more stuff>

UIWebView doesn't support multiple windows so it can't postMessage back to your original page since it's no longer loaded. What you can do is detect when the UIWebView is trying to load the FB login page and load that in a separate UIWebView. Now you have two windows to work with.

Unfortunately, this is still not enough as when the JavaScript on FB's page tries to run window.opener.postMessage or window.parent.postMessage it doesn't work because window.parent and window.opener aren't set to the appropriate window. I don't know of a good way to do this in iOS (in contrast Android provides a proper API for this).

The way I've worked around this is to hack up a JavaScript object to wrap these calls. Something like:

window.opener={};
window.opener.postMessage = function(data,url) {
    // signal your code in objective-c using some strategy
};
window.parent = window.opener;

There are a few ways you can call Objective-C from JavaScript including this one from the official docs. You can inject this code into that static FB login page I mentioned before using stringByEvaluatingJavaScriptFromString:. I couldn't find a good time to do this, so I just inject it after page load and call doFragmentSend() which is the FB JavaScript method on that static page that normally gets called on body load.

So now all we need to do is pass on this data into the original UIWebView by calling postMessage. It'll look something like this:

NSString *post = [NSString stringWithFormat:@"window.postMessage('%@', '*');", data];
[webView stringByEvaluatingJavaScriptFromString:post];

If you haven't noticed by now, this is a huge messy hack and I probably wouldn't recommend it unless you have no alternative, but it's worked for me.

I had the same problem. I figured out, after the Facebook login the UIWebView components is empty, there is no html code within it. I solved the issue by checking the content of the UIWebView component on the webViewDidFinishLoad function and reload its content when I detect the Facebook login causes white (empty screen):

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    if ( [[webView1 stringByEvaluatingJavaScriptFromString:
           @"document.body.innerHTML"] isEqualToString:@""] ) {
        [webView1 loadRequest:request]; //Define request as you want
    }
}

Currently in mobile, browser not supporting multiple windows by default. The alternate solution is to monitor the successful redirect from facebook like "close_popup.php?reload=https://" and reloading the page. Also make sure that you preserve the facebook comment value before login request so that it can be again put in comment box.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!