Search code examples
ruby-on-railsoauth-2.0devisedevise-token-authomniauth-google-oauth2

How to send sign in info to frontend after successful Oauth2 authorization in devise_token_auth?


I'm using devise_token_auth for Email and Google Oauth2 based authentication(used omniauth-google-oauth2 gem for this). I've successfully managed to store sign in info of the user signing up/in through Google Oauth2 flow. The info includes:

{"auth_token"=>"token here", "client_id"=>"client id here", "uid"=>"uid here", "expiry"=>1620492005, "config"=>nil, "oauth_registration"=>true}

The flow for the above info was

  1. Visit http://localhost:3000/auth/google_oauth2. This redirects you to the Google auth screen
  2. User selects account and grants permission.
  3. Oauth success callback from my app is executed at http://localhost:3000/auth/google_oauth2/callback

The code which executes for the first step is

module DeviseTokenAuth
  class OmniauthCallbacksController < DeviseTokenAuth::ApplicationController
    attr_reader :auth_params

    before_action :validate_auth_origin_url_param
    
    def omniauth_success
      get_resource_from_auth_hash
      set_token_on_resource
      create_auth_params

      if confirmable_enabled?
        # 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
  end
end

Problems I'm facing:

  1. sign_in method call does not set @current_user despite that @resource and @auth_params have all the necessary info in them.
  2. How can I inform my frontend app about the sign in info(token, client_id, uid)?
render_data_or_redirect('deliverCredentials', @auth_params.as_json, @resource.as_json)

this call does not redirect or render anything, instead it stays on the same page the URL it shows is http://localhost:3000/auth/google_oauth2/callback#

I basically have three questions now:

  1. How can I use devise_token_auth to set the current_user based on the incoming auth headers? I have added the following line to my controller, but still it fails to set @current_user
include DeviseTokenAuth::Concerns::SetUserByToken

maybe it is because I'm sending auth headers incorrectly? See my 3rd point below for this.

  1. How am I supposed to send the sign in info to my frontend app? Do I modify the above method somehow to send sign in info to my frontend app?

  2. What and where do I put the auth headers in order to make authenticated requests?

  • When using devise_token_auth with email as auth provider I have to send 3 pieces to make an authenticated request i.e access-token, client_id and uid
  • Now in case of providers like Google/Facebook etc, do I set all of these headers or not?

I have used postman to test both of the following but it failed with Unauthorized error

  1. Sent access-token, client_id and uid in headers
  2. Sent Bearer my_token in authorization headers.

Solution

  • To get this working, we had to override the DeviseTokenAuth's OmniAuthCallbacks controller and update its render_data_or_redirect method.

    The default definition of render_data_or_redirect is

    def render_data_or_redirect(message, data, user_data = {})
      if ['inAppBrowser', 'newWindow'].include?(omniauth_window_type)
        render_data(message, user_data.merge(data))
      elsif auth_origin_url
    
        redirect_to DeviseTokenAuth::Url.generate(auth_origin_url, data.merge(blank: true))
      else
        fallback_render data[:error] || 'An error occurred'
      end
    end
    
    

    The following routes.rb and custom_omniauth_callbacks_controller.rb have the changes needed to be done to get it working with j-toker library.

    In the routes file

    # config/routes.rb
    
    mount_devise_token_auth_for 'User', at: 'auth', controllers: {
      omniauth_callbacks: "devise_token_auth/custom_omniauth_callbacks"
    }
    

    and the definition of CustomOmniAuthCallbacksController was

    # app/controllers/devise_token_auth/custom_omniauth_callbacks_controller.rb
    
    module DeviseTokenAuth
      class CustomOmniauthCallbacksController < DeviseTokenAuth::OmniauthCallbacksController
        protected
    
        def render_data_or_redirect(message, data, user_data = {})
          if (['inAppBrowser', 'newWindow'].include?(omniauth_window_type) || auth_origin_url)
    
            redirect_to DeviseTokenAuth::Url.generate(auth_origin_url, data.merge(blank: true))
          else
            fallback_render data[:error] || 'An error occurred'
          end
        end
      end
    end
    
    

    now on the front-end side you need to configure j-toker

    First, install j-toker package, yarn package or npm package

    yarn add j-toker
    

    or

    npm i j-toker
    

    then in your javascript application, configure the j-toker

    import $ from "jquery";
    
    $.auth.configure({
        apiUrl: "https://your-api-domain.com",
        emailSignInPath: "/auth/sign_in",
        signOutPath: "/auth/sign_out",
        emailRegistrationPath: "/auth",
        tokenValidationPath: "/auth/validate_token"
    
        authProviderPaths: {
          facebook: "/auth/facebook",
          google: "/auth/google_oauth2"
        }
      });