diff --git a/app/controllers/v1/omniauth_callbacks_controller.rb b/app/controllers/v1/omniauth_callbacks_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4bffba452e309a0916576aae140a5d7e07f0d6fb
--- /dev/null
+++ b/app/controllers/v1/omniauth_callbacks_controller.rb
@@ -0,0 +1,254 @@
+  class V1::OmniauthCallbacksController < DeviseTokenAuth::ApplicationController
+
+    attr_reader :auth_params
+    skip_before_action :set_user_by_token, raise: false
+    skip_after_action :update_auth_header
+
+    # intermediary route for successful omniauth authentication. omniauth does
+    # not support multiple models, so we must resort to this terrible hack.
+    def redirect_callbacks
+
+      # derive target redirect route from 'resource_class' param, which was set
+      # before authentication.
+      devise_mapping = [request.env['omniauth.params']['namespace_name'],
+                        request.env['omniauth.params']['resource_class'].underscore.gsub('/', '_')].compact.join('_')
+      redirect_route = "#{request.protocol}#{request.host_with_port}/#{Devise.mappings[devise_mapping.to_sym].fullpath}/#{params[:provider]}/callback"
+
+      # preserve omniauth info for success route. ignore 'extra' in twitter
+      # auth response to avoid CookieOverflow.
+      session['dta.omniauth.auth'] = request.env['omniauth.auth'].except('extra')
+      session['dta.omniauth.params'] = request.env['omniauth.params']
+
+      redirect_to redirect_route
+    end
+
+    def omniauth_success
+      get_resource_from_auth_hash
+      create_token_info
+      set_token_on_resource
+      create_auth_params
+
+      if resource_class.devise_modules.include?(:confirmable)
+        # don't send confirmation email!!!
+        @resource.skip_confirmation!
+      end
+
+      sign_in(:user, @resource, store: false, bypass: false)
+
+      @resource.save!
+
+      yield @resource if block_given?
+
+      render_data_or_redirect('deliverCredentials', @auth_params.as_json, @resource.as_json)
+    end
+
+    def omniauth_failure
+      @error = params[:message]
+      render_data_or_redirect('authFailure', {error: @error})
+    end
+
+    protected
+
+    # this will be determined differently depending on the action that calls
+    # it. redirect_callbacks is called upon returning from successful omniauth
+    # authentication, and the target params live in an omniauth-specific
+    # request.env variable. this variable is then persisted thru the redirect
+    # using our own dta.omniauth.params session var. the omniauth_success
+    # method will access that session var and then destroy it immediately
+    # after use.  In the failure case, finally, the omniauth params
+    # are added as query params in our monkey patch to OmniAuth in engine.rb
+    def omniauth_params
+      if !defined?(@_omniauth_params)
+        if request.env['omniauth.params'] && request.env['omniauth.params'].any?
+          @_omniauth_params = request.env['omniauth.params']
+        elsif session['dta.omniauth.params'] && session['dta.omniauth.params'].any?
+          @_omniauth_params ||= session.delete('dta.omniauth.params')
+          @_omniauth_params
+        elsif params['omniauth_window_type']
+          @_omniauth_params = params.slice('omniauth_window_type', 'auth_origin_url', 'resource_class', 'origin')
+        else
+          @_omniauth_params = {}
+        end
+      end
+      @_omniauth_params
+
+    end
+
+    # break out provider attribute assignment for easy method extension
+    def assign_provider_attrs(user, auth_hash)
+      user.assign_attributes({
+        nickname: auth_hash['info']['nickname'],
+        name:     auth_hash['info']['name'],
+        avatar:   auth_hash['info']['image'],
+        email:    auth_hash['info']['email']
+      })
+    end
+
+    # derive allowed params from the standard devise parameter sanitizer
+    def whitelisted_params
+      whitelist = params_for_resource(:sign_up)
+
+      whitelist.inject({}){|coll, key|
+        param = omniauth_params[key.to_s]
+        if param
+          coll[key] = param
+        end
+        coll
+      }
+    end
+
+    def resource_class(mapping = nil)
+      if omniauth_params['resource_class']
+        omniauth_params['resource_class'].constantize
+      elsif params['resource_class']
+        params['resource_class'].constantize
+      else
+        raise "No resource_class found"
+      end
+    end
+
+    def resource_name
+      resource_class
+    end
+
+    def omniauth_window_type
+      omniauth_params['omniauth_window_type']
+    end
+
+    def auth_origin_url
+      omniauth_params['auth_origin_url'] || omniauth_params['origin']
+    end
+
+    # in the success case, omniauth_window_type is in the omniauth_params.
+    # in the failure case, it is in a query param.  See monkey patch above
+    def omniauth_window_type
+      omniauth_params.nil? ? params['omniauth_window_type'] : omniauth_params['omniauth_window_type']
+    end
+
+    # this sesison value is set by the redirect_callbacks method. its purpose
+    # is to persist the omniauth auth hash value thru a redirect. the value
+    # must be destroyed immediatly after it is accessed by omniauth_success
+    def auth_hash
+      @_auth_hash ||= session.delete('dta.omniauth.auth')
+      @_auth_hash
+    end
+
+    # ensure that this controller responds to :devise_controller? conditionals.
+    # this is used primarily for access to the parameter sanitizers.
+    def assert_is_devise_resource!
+      true
+    end
+
+    # necessary for access to devise_parameter_sanitizers
+    def devise_mapping
+      if omniauth_params
+        Devise.mappings[[omniauth_params['namespace_name'],
+                         omniauth_params['resource_class'].underscore].compact.join('_').to_sym]
+      else
+        request.env['devise.mapping']
+      end
+    end
+
+    def set_random_password
+      # set crazy password for new oauth users. this is only used to prevent
+        # access via email sign-in.
+        p = SecureRandom.urlsafe_base64(nil, false)
+        @resource.password = p
+        @resource.password_confirmation = p
+    end
+
+    def create_token_info
+      # create token info
+      @client_id = SecureRandom.urlsafe_base64(nil, false)
+      @token     = SecureRandom.urlsafe_base64(nil, false)
+      @expiry    = (Time.now + DeviseTokenAuth.token_lifespan).to_i
+      @config    = omniauth_params['config_name']
+    end
+
+    def create_auth_params
+      @auth_params = {
+        auth_token:     @token,
+        client_id: @client_id,
+        uid:       @resource.uid,
+        expiry:    @expiry,
+        config:    @config
+      }
+      @auth_params.merge!(oauth_registration: true) if @oauth_registration
+      @auth_params
+    end
+
+    def set_token_on_resource
+      @resource.tokens[@client_id] = {
+        token: BCrypt::Password.create(@token),
+        expiry: @expiry
+      }
+    end
+
+    def render_data(message, data)
+      @data = data.merge({
+        message: message
+      })
+      render :layout => nil, :template => "devise_token_auth/omniauth_external_window"
+    end
+
+    def render_data_or_redirect(message, data, user_data = {})
+
+      # We handle inAppBrowser and newWindow the same, but it is nice
+      # to support values in case people need custom implementations for each case
+      # (For example, nbrustein does not allow new users to be created if logging in with
+      # an inAppBrowser)
+      #
+      # See app/views/devise_token_auth/omniauth_external_window.html.erb to understand
+      # why we can handle these both the same.  The view is setup to handle both cases
+      # at the same time.
+      if ['inAppBrowser', 'newWindow'].include?(omniauth_window_type)
+        render_data(message, user_data.merge(data))
+
+      elsif auth_origin_url # default to same-window implementation, which forwards back to auth_origin_url
+
+        # build and redirect to destination url
+        redirect_to DeviseTokenAuth::Url.generate(auth_origin_url, data.merge(blank: true))
+      else
+
+        # there SHOULD always be an auth_origin_url, but if someone does something silly
+        # like coming straight to this url or refreshing the page at the wrong time, there may not be one.
+        # In that case, just render in plain text the error message if there is one or otherwise
+        # a generic message.
+        fallback_render data[:error] || 'An error occurred'
+      end
+    end
+
+    def fallback_render(text)
+        render inline: %Q|
+
+            <html>
+                    <head></head>
+                    <body>
+                            #{text}
+                    </body>
+            </html>|
+    end
+
+    def get_resource_from_auth_hash
+      # find or create user by provider and provider uid
+      @resource = resource_class.where({
+        uid:      auth_hash['uid'],
+        provider: auth_hash['provider']
+      }).first_or_initialize
+
+      if @resource.new_record?
+        @oauth_registration = true
+        set_random_password
+      end
+
+      # sync user info with provider, update/generate auth token
+      assign_provider_attrs(@resource, auth_hash)
+
+      # assign any additional (whitelisted) attributes
+      extra_params = whitelisted_params
+      @resource.assign_attributes(extra_params) if extra_params
+
+      @resource
+    end
+
+  end
diff --git a/config/initializers/devise_token_auth.rb b/config/initializers/devise_token_auth.rb
index ba76d510ae984c0236119db596223b166f04a930..13b395649a738df1c0c4b0eb19fe5a41a6622f2a 100644
--- a/config/initializers/devise_token_auth.rb
+++ b/config/initializers/devise_token_auth.rb
@@ -22,7 +22,7 @@ DeviseTokenAuth.setup do |config|
   # This route will be the prefix for all oauth2 redirect callbacks. For
   # example, using the default '/omniauth', the github oauth2 provider will
   # redirect successful authentications to '/omniauth/github/callback'
-  # config.omniauth_prefix = "/omniauth"
+  config.omniauth_prefix = "/omniauth"
 
   # By default sending current password is not needed for the password update.
   # Uncomment to enforce current_password param to be checked before all
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
index 8863174a07d6df1a886558eb45f381b4b5c120be..1ed6c505fd34e364209e98ed848648c6a99bb98e 100644
--- a/config/initializers/omniauth.rb
+++ b/config/initializers/omniauth.rb
@@ -1,5 +1,5 @@
 Rails.application.config.middleware.use OmniAuth::Builder do
   provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET']
-  provider :facebook,      ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET']
+  provider :facebook,      ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'], secure_image_url: true
   provider :google_oauth2, ENV['GOOGLE_KEY'],   ENV['GOOGLE_SECRET']
 end
diff --git a/config/routes.rb b/config/routes.rb
index c79ff197ec8a3f4249527e1024b4a19e39de901f..8f18a468949fa5def74f85f661490431443612a9 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -67,7 +67,9 @@ Rails.application.routes.draw do
   end
 
   scope :v1 do
-    mount_devise_token_auth_for 'User', at: :auth
+    mount_devise_token_auth_for 'User', at: 'auth', controllers: {
+      omniauth_callbacks: 'v1/omniauth_callbacks'
+    }
   end
 
   namespace :v1 do
diff --git a/spec/controllers/v1/omniauth_callbacks_controller_spec.rb b/spec/controllers/v1/omniauth_callbacks_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a7bb409edcb96cebfd6491d500b11824d5a06f11
--- /dev/null
+++ b/spec/controllers/v1/omniauth_callbacks_controller_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe V1::OmniauthCallbacksController, type: :controller do
+
+end