ruby-on-railsfacebookdeviseomniauthomniauth-facebook

Devise/OmniAuth Override default callback url


I'm using Devise 3.5 with Omniauth in a Rails 4 app. I've created an integration with Facebook that allows a user to connect their Facebook account to my app. Currently when the user clicks the connect button, they're sent to /user/auth/facebook and then redirected to the callback url that Omniauth generates: /user/auth/facebook/callback. What I'd like to do is manually override this callback url in some cases - meaning that I don't want to override it in an initializer - with a fully qualified url. For example, if a user starts out on http://www.example.com/ I might want to override the default callback url with http://app.example.com/user/auth/facebook/callback.

My app has dynamic subdomains and a user will (almost) always begin the authentication process on a subdomain. Unfortunately it seems that Facebook doesn't support wildcards in oauth redirect urls, which is why I want the ability to detect if a user is on a subdomain and adjust the callback url to something that I have whitelisted on my Facebook app so that the authorization process succeeds.

From what I've read, the url helper omniauth_authorize_path accepts additional arguments to be passed on as parameters. I've tried passing a custom callback path in like so, but without success:

user_omniauth_authorize_path(:facebook, callback_path: @custom_callback)

I've also tried changing callback_path to redirect_url and redirect_uri, but nothing seems to work. When I look at the link that's generated, it does indeed include the callback as a parameter in the url, but when I click the link, I'm redirected back to the default callback url instead of the custom callback url.


Solution

  • Here's how I solved this problem. I'm sure there are other ways, but this seems like the simplest most elegant solution I could come up with.

    In config/routes.rb I set up an auth subdomain. All my Oauth connect requests will start on different subdomains and then Facebook is set up to forward those users back to the auth.example.com subdomain.

    constraints AuthRedirect do
        devise_scope :contact do
            get '/auth/facebook/callback' => 'omniauth_callbacks#facebook'
            post '/auth/facebook/callback' => 'omniauth_callbacks#facebook'
        end
    end
    

    Here is /lib/auth_redirect.rb. This just checks if the subdomain is auth and captures that traffic. This is placed at the top of my routes list so as to take precedence over other subdomains.

    class AuthRedirect
        def self.matches?(request)
            request.subdomain.present? && request.subdomain == 'auth'
        end
    end
    

    Then in my client, when a user clicks the Connect with Facebook button, I send them to /auth/facebook?contact_id=<id>. From here Devise directs them to Facebook, which then redirects them back to https://auth.example.com/.

    Then in OmniauthCallbacksController#facebook I can pull the user's id from the omniauth params like so:

    auth = env["omniauth.auth"]
    contact = Contact.find(env['omniauth.params']['contact_id'])
    

    From here I can persist the credentials to the database and the redirect the user back to the appropriate subdomain. This solution avoids problems with CSRF tokens and more importantly does not require me to use Ruby/ERB to build the omniauth authorize path that the user is sent to when they click the connect button.