turbolinks / turbolinks-ios

Native iOS adapter for building hybrid apps with Turbolinks 5
MIT License
881 stars 92 forks source link

How to login with omniauth-google-oauth2 or omniauth-facebook #66

Closed chamnap closed 8 years ago

chamnap commented 8 years ago

I'm a newbie to swift and ios. I'm converting the rails app into turbolinks. Everything is fine, but when it comes to integrate login with facebook or google. It's just doesn't work. I understand that link is NOT turbolink url, therefore it doesn't work. It's under different UIViewController, AuthenticationController.

Is there any clue?

apbendi commented 8 years ago

I'd love some guidance on this as well. Omniauth seems to be doing a 302 out to the service in question, which makes sense, at which point Turbolinks gives me a session:didFailRequestForVisitable:withError: callback with a status code of 0. I can't pin down where in the stack that error is being generated from, or how to capture it and work around it in any way.

zachwaugh commented 8 years ago

I'm haven't used Omniauth, so I can't say for sure without some more details about the authentication flow. If it loads an external web service or any page that doesn't use Turbolinks, you won't be able to load it through the Turbolinks framework. You most likely will need to create a separate webview that uses the same WKProcessPool so the cookies are shared between the authentication view and the turbolinks webview. There's an example of this in the demo app - https://github.com/turbolinks/turbolinks-ios/blob/master/TurbolinksDemo/AuthenticationController.swift

apbendi commented 8 years ago

Thanks! That makes sense @zachwaugh, and was my initial thought as well. What I was hoping was that there would be a way to catch the failure reliably in the callback state, with some kind of reliable error code that indicated we attempted to redirect to external website. If I had that, I could subsequently load and display a generic webview in any of those cases. But all I'm getting back is a 0 status code and a message of A network error occurred..

I guess I'll have to settle for special casing a handful of our URLs in session:didProposeVisitToURL:withAction: that we know will redirect, and override with a generic webview at that point. Unless you have any other suggestions? Thanks!

lgrimes commented 8 years ago

Just wanting to link to this issue over on the rails-turbolinks github in case it solves this problem for anyone.

michiels commented 8 years ago

I just implemented an Oauth flow in our app last night. It's not a 100% answer to your OmniAuth, but what I did is as follows:

apbendi commented 8 years ago

For what its worth for anyone whose app does use OmniAuth, I was able to get this working inside the app using Turbolinks. Basically, in the Turbolinks session:didProposeVisitToURL:withAction: callback, I capture certain URLs that I know will result in an OmniAuth request, then rather than visit them, I present a new view controller that contains a stock WKWebView that is configured to share the same process pool as my Turbolinks Session. Inside the WKWebView delegate, I detect when the OAuth flow is redirecting back to our app's site, and instead of loading that URL I dismiss the webview and kick the URL back to the Turbolinks Session, which now has the appropriate cookies set. Works just fine.

chamnap commented 8 years ago

What I did is similar to @apbendi:

    func session(session: Session, didProposeVisitToURL URL: NSURL, withAction action: Action) {
        if URL.path == "/auth/google_oauth2" ||
            URL.path == "/auth/microsoft_office365" ||
            URL.path == "/auth/facebook" {
            presentAuthenticationController(URL)
        } else {
            presentVisitableForSession(session, URL: URL, action: action)
        }
    }

Inside the AuthenticationController:

    func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {

        let URL = navigationAction.request.URL!
        if URL.absoluteString == "http://localhost:3000/" { //url that is redirected back to your app
            decisionHandler(.Cancel)
            delegate?.authenticationControllerDidAuthenticate(self)
            return
        }

        decisionHandler(.Allow)
    }