Facebook authentication in a UIWebView does not redirect back to original page on my site asking for auth
Solution 1
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
}
}
Solution 2
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.
Solution 3
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
}
}
TMC
Updated on June 19, 2022Comments
-
TMC almost 2 years
In our iOS app, we have a UIWebView that shows web content on our domain that has a Facebook comment module. The comment module requires that the user is signed in with facebook. When user clicks on the sign in button, they are taken through the sign in flow, but are never redirect back to our page. They end up on an FB owned page that just tells the user "You are now signed in".
Repro steps:
- Create a UIWebView in an iOS app, and host a Facebook comment module on a page hosted on some domain you own (e.g. http://foo.com/test.htm).
- Click on the Sign In button on the comment module and notice you are redirect to FB sign in.
- Sign in with valid FB credentials and observe what happens.
After you sign in (step 3) I would expect that after a successful authentication, you are redirected back to the original page (e.g http://foo.com/test.htm) so you can continue your interaction. However, this isn't happening.
Instead, you are on an FB owned page that just says something like "You are now signed in" and you are trapped there. No redirect happens.
Is this indeed a bug or is there something else I should be doing to ensure the redirect happens?
-
theShay over 10 yearsI have one question. how you enter the
window.opener={}; window.opener.postMessage = function(data,url) { // signal your code in objective-c using some strategy }; window.parent = window.opener;
code to the page? -
kabuko over 10 yearsThe paragraph below that code block describes how to inject that code.
-
Guy in the chair about 9 yearsHey I know it's an old post, but this might be the solution to my query. Can you share what to put in
request
in a line[webView1 loadRequest:request]
. Sorry I'm new to iOS -
rAzOr almost 9 years@ShanilSoni: Did u figure out what is request??
-
rAzOr almost 9 years@szpetip: Would you please mention what request is, with a sample??
-
Guy in the chair almost 9 years@rAzOr Nope, I didn't. Please share.
-
Radu Vlad over 7 yearswhat is popupWebView here?
-
dthulke over 7 yearsIt is a new WKWebView instance which is generated in the method
createWebViewWithConfiguration
. This web view is used the "new window" in which the Facebook login is going to be displayed. I am going to update the answer to make this more clear. -
Radu Vlad over 7 yearsThanks very much. This should be the new response as it is much easier. Just a thing, u forgot about
webView?.UIDelegate = self
-
dthulke over 7 yearsThanks, you're right. I just added it to the code snippet.
-
Supertecnoboff over 6 yearsThis does NOT detect Google/Facebook/etc.. login popups/windows. I tried it and it doesn't work. It does detect basic external website links, but when pressing on the
Login with Facebook
button, it just loads the Facebook login page in the currentWKWebView
. Is there any solution for this? -
Supertecnoboff over 6 yearsIn regards to my last comment, this works on MOST websites. For example I was testing it out with the Feedly website and it didn't work.
-
Jamshed Alam about 5 years@ dthulke, i have successfully created a new instance of wkwebview and loggin is successful now. After login with facebook , i have some button that share data in facebook. When click that , wkwebview createWebViewWith method is called. If i create new window/instance with the share url , it just open facebook home page. Not share view. What could be the solution for this case ?
-
dthulke about 5 yearsJust a wild guess (since I do not know how your logic is working): probably there is no redirect in the newly opened window and the popup is expected to be closed somehow. Did you check whether webViewDidClose is called in the popup and do you handle this appropriately? If this does not help please open a new question.