module MnoEnterprise::Concerns::Controllers::Auth::OmniauthCallbacksController

This controller is used to handle the authentication (+creation) of external users via OpenID (e.g: QuickBooks OpenID)

When users click on the “sign in with <provider>” button, they get redirected to the authorize endpoint (/users/auth/:provider - e.g: /users/auth/intuit). The action (handled by parent controller OmniauthCallbacksController) prepares the callback url then redirects the user to the OpenID provider (E.g: Intuit) for authentication.

Once authentication has been performed at the OpenID provider level (e.g: Intuit) the user gets redirected to the callback endpoint (/users/auth/:provider/callback) The provider parameter in the url (E.g: intuit) gets automatically redirected to a controller action with the same name (handled by parent controller OmniauthCallbacksController) as you can see below with intuit.

Then provider specific action then handles the (creation +) authentication of the user. Also, it automatically adds the right applications to the user dashboard (e.g: QuickBooks for Intuit)

Intuit:


For intuit, the authorize endpoint is be bypassed when the user clicks “try Maestrano” from the Intuit marketplace. The user automatically lands on the callback endpoints with a parameter in the url called 'qb_initiated'. This parameter is used to automatically trigger the retrieval of the oauth token in the background via javascript (by storing the temporary grant url in session)

On Intuit, it is also possible to directly choose one of the apps proposed by Maestrano (E.g: 'SugarCRM by Maestrano'). In this case, an 'app' attribute containing the app nid (named id - e.g: 'sugarcrm') is added to the url parameters. The action then setup the app automatically (along with QuickBooks).

Public Instance Methods

intuit() click to toggle source
Instance methods
GET|POST /users/auth/:action/callback
# File lib/mno_enterprise/concerns/controllers/auth/omniauth_callbacks_controller.rb, line 76
def intuit
  auth = request.env['omniauth.auth']
  opts = {
    orga_on_create: create_orga_on_user_creation(auth.info.email),
    authorized_link_to_email: session['omniauth.intuit.authorized_link_to_email']
  }

  # Try to find via intuit
  begin
    @user = MnoEnterprise::User.find_for_oauth(auth, opts, current_user)
  rescue SecurityError
    # Intuit email is NOT a confirmed email. Therefore we need to ask the user to
    # login the old fashion to make sure it is the right user!
    session["omniauth.intuit.request_account_link"] = true
    redirect_to new_user_session_path, notice: "Please sign in using your regular Maestrano account to confirm that you want to link it to your Intuit account"
    return
  end

  # Cleanup any temporary omniauth.intuit session
  cleanup_intuit_session

  if @user && @user.persisted?
    # Automatically adds a QuickBooks app (and any other app passed via :app param)
    # to the user orga
    # Only for new users for which an orga was created (not an invited user
    # typically)
    app_instances = setup_apps(@user,['quickbooks',params[:app]], oauth_keyset: params[:app])
    qb_instance = app_instances.first

    # On Intuit, Mno is configured to add qb_initiated=true if the user
    # comes directly from apps.com (This is a different workflow from using
    # the QuickBooks connect button because we're supposed to trigger the
    # oauth workflow directly via javascript using directConnectToIntuit)
    # Here we store in session the fact that we need to trigger an oauth
    # workflow via directConnectToIntuit
    # ----
    # See layouts/partners/intuit for more info. The session param set
    # below get reset in the view.
    #
    if params[:qb_initiated] && qb_instance && !qb_instance.oauth_keys_valid?
      session[:qb_direct_connect_grant_url] = authorize_webhook_oauth_url(qb_instance.uid)
    end

    # The above methods trigger many different hooks which
    # may impact the user (typically user workspace). It is safer
    # to reload the user before continuing so that the picture
    # is up to date
    @user.reload

    # Check whether we should redirect the user to a specific
    # url
    redirect_url = session.delete(:openid_previous_url) || MnoEnterprise.router.dashboard_path || main_app.root_path

    sign_in @user
    redirect_to redirect_url, event: :authentication

    set_flash_message(:notice, :success, kind: "Intuit") if is_navigational_format?
  else
    session["devise.intuit_data"] = request.env["omniauth.auth"]
    redirect_to home_url, "ng-controller" => "MnoSignupProcessCtrl", "ng-click" => "startProcess()"
  end
end

Private Instance Methods

cleanup_intuit_session() click to toggle source
Private methods
# File lib/mno_enterprise/concerns/controllers/auth/omniauth_callbacks_controller.rb, line 144
def cleanup_intuit_session
  session.delete("omniauth.intuit.passthru_email")
  session.delete("omniauth.intuit.request_account_link")
end
create_orga_on_user_creation(user_email = nil) click to toggle source

Whether to create an orga on user creation

# File lib/mno_enterprise/concerns/controllers/auth/omniauth_callbacks_controller.rb, line 150
def create_orga_on_user_creation(user_email = nil)
  return false if user_email.blank?
  return false if MnoEnterprise::User.exists?(email: user_email)

  # First check previous url to see if the user
  # was trying to accept an orga
  if !session[:previous_url].blank? && (r = session[:previous_url].match(/\/orga_invites\/(\d+)\?token=(\w+)/))
    invite_params = { id: r.captures[0].to_i, token: r.captures[1] }
    return false if OrgInvite.where(invite_params).any?
  end

  # Get remaining invites via email address
  return MnoEnterprise::OrgInvite.where(user_email: user_email).empty?
end
setup_apps(user = nil, app_nids = [], opts = {}) click to toggle source

Create or find the apps provided in argument Accept an array of app nid (named id - e.g: 'quickbooks') opts:

oauth_keyset: If a oauth_keyset is provided then it will be added to the

oauth_keys of any app that is oauth ready (QuickBooks for example)

Return an array of app instances (found or created)

# File lib/mno_enterprise/concerns/controllers/auth/omniauth_callbacks_controller.rb, line 172
def setup_apps(user = nil, app_nids = [], opts = {})
  return [] unless user
  return [] unless (user.organizations.reload.count == 1)
  return [] unless (org = user.organizations.first)
  return [] unless MnoEnterprise::Ability.new(user).can?(:edit,org)

  results = []

  apps = MnoEnterprise::App.where('nid.in' => app_nids.compact)
  existing = org.app_instances.active.index_by(&:app_id)

  # For each app nid (which is not nil), try to find an existing instance or create one
  apps.each do |app|
    if (app_instance = existing[app.id])
      results << app_instance
    else
      # Provision instance and add to results
      app_instance = org.app_instances.create(product: app.nid)
      results << app_instance
      MnoEnterprise::EventLogger.info('app_add', user.id, 'App added', app_instance)
    end

    # Add oauth keyset if defined and app_instance is
    # oauth ready and does not have a valid set of oauth keys
    if app_instance && opts[:oauth_keyset].present? && !app_instance.oauth_keys_valid?
      app_instance.oauth_keys = { keyset: opts[:oauth_keyset] }
      app_instance.save
    end
  end
  return results
end