module Authbox

This module adds support for Authbox to your Rails controller. We recommend you add it to app/controllers/application_controller.rb like this:

require 'authbox'

class ApplicationController < ActionController::Base
 include Authbox
end

Once this is included in your controller, you’ll need to add your credentials. Add them to config/application.rb like this:

config.authbox = {
  :api_key => 'yourApiKey',
  :secret_key => 'yourSecret'
}

That’s it! Authbox can start logging basic metadata immediately.

To get the most out of Authbox you should tell us who your users are. You do this by overriding the authbox_get_request_data() method on the controller. Here’s how you would tell Authbox about your users while using Devise:

class ApplicationController < ActionController::Base
  include Authbox

  def authbox_get_request_data
    return {
      '$user' => {
        '$creationTime' => current_user.created_at,
        '$userIDs' => [
          {'$type' => '$email', '$key' => current_user.email}
        ]
      }
    }
  end
end

Private Class Methods

authbox_pool() click to toggle source
# File lib/authbox.rb, line 105
def self.authbox_pool
  if @authbox_pool.nil?
    @authbox_pool = Thread.pool(Rails.configuration.authbox[:threads] || 5)
  end
  return @authbox_pool
end

Public Instance Methods

authbox_check(action={}) click to toggle source
# File lib/authbox.rb, line 70
def authbox_check(action={})
  if not authbox_ensure_one_request(:check)
    return @authbox_verdict
  end

  @authbox_check_action = action
  return authbox_request('/action_check', action, false)
end
authbox_get_request_data() click to toggle source

Override me to return additional data for the request (like the user)

# File lib/authbox.rb, line 99
def authbox_get_request_data
  return {}
end
authbox_log(action={}) click to toggle source

Report a custom action to Authbox. If this is not called during a request, an $unknown action will be logged. See the documentation for what information you can pass here in the action hash.

# File lib/authbox.rb, line 57
def authbox_log(action={})
  if not authbox_ensure_one_request(:log)
    return
  end

  if not @authbox_verdict.blank? and @authbox_verdict['type'] != 'ALLOW'
    return
  end


  return authbox_request('/action_log', action, true)
end
authbox_post_form(uri, body) click to toggle source

Override me to inject a custom HTTP POST library

# File lib/authbox.rb, line 81
def authbox_post_form(uri, body)
  authbox_debug_log {"Posting data to #{uri}: #{body}"}

  req = Net::HTTP::Post.new(uri.path, initheader = {'Content-Type' =>'application/json'})
  req.body = body.to_json

  return Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
    begin
      http.request(req)
    rescue => e
      authbox_warn_log { "HTTP request error: #{e}" }
    end
  end
end

Private Instance Methods

authbox_after() click to toggle source
# File lib/authbox.rb, line 245
def authbox_after
  if not @authbox_fired[:log]
    authbox_log @authbox_check_action

    if @authbox_insert_pixel and Rails.configuration.authbox.fetch(:enable_tracking_pixel, true)
      endpoint = authbox_get_endpoint()
      local_machine_id = cookies[authbox_get_cookie('local_machine_id')]
      pixel_markup = "<iframe src='#{endpoint}/pixel?LMID=#{local_machine_id}' width='0' height='0' style='border: none' />"

      prev_length = response.body.length
      response.body = response.body.gsub(/(<\/body>)/i, pixel_markup + '\1')

      if response.body.length > prev_length
        # we actually inserted the pixel, so send the cookie
        cookies[authbox_get_cookie('did_get_pixel')] = '1'
      end
    end
  end
end
authbox_before() click to toggle source
# File lib/authbox.rb, line 236
def authbox_before
  @authbox_fired = {
    :check => false,
    :log => false
  }
  @authbox_check_action = {}
  @authbox_insert_pixel = rand() < 0.01
end
authbox_debug_log(&message_block) click to toggle source
# File lib/authbox.rb, line 112
def authbox_debug_log(&message_block)
  # Rails doesn't have a great way to filter logging out of the box and we want
  # this SDK to be as out-of-the-way as possible, so we gate all logging behind a
  # config.
  if Rails.configuration.authbox[:debug]
    logger.tagged('AUTHBOX') {
      logger.debug message_block
    }
  end
end
authbox_ensure_one_request(type) click to toggle source
# File lib/authbox.rb, line 140
def authbox_ensure_one_request(type)
  if @authbox_fired[type]
    authbox_warn_log { 'authbox_log() already called' }
    return false
  end

  @authbox_fired[type] = true
  return true
end
authbox_get_endpoint() click to toggle source
# File lib/authbox.rb, line 136
def authbox_get_endpoint
  return Rails.configuration.authbox[:endpoint] || 'https://api.authbox.io/api'
end
authbox_request(endpoint, action, async) click to toggle source
# File lib/authbox.rb, line 150
def authbox_request(endpoint, action, async)
  cookie_name = authbox_get_cookie('local_machine_id')

  if Rails.configuration.authbox[:api_key].nil? or Rails.configuration.authbox[:secret_key].nil?
    authbox_warn_log { 'api_key and secret_key were not set' }
  end

  if cookies[cookie_name].blank?
    local_machine_id = SecureRandom.hex(32)
    @authbox_new_local_machine_id = true
    @authbox_insert_pixel = true
  else
    local_machine_id = cookies[cookie_name]
    @authbox_new_local_machine_id = false
  end

  cookies[cookie_name] = {
    :value => local_machine_id,
    :expires => 2.years.from_now,
    :httponly => true,
    :domain => :all
  }

  if cookies[authbox_get_cookie('did_get_pixel')].blank?
    @authbox_insert_pixel = true
  end

  remote_ip = request.remote_ip

  body = {
    '$actionName' => '$unknown',
    '$localMachineID' => {
      '$key' => local_machine_id,
      '$new' => @authbox_new_local_machine_id
    },
    '$userAgent' => request.user_agent,
    '$host' => request.host,
    '$referer' => request.referer,
    '$ipAddress' => remote_ip,
    '$endpointURL' => request.original_url,
    '$apiKey' => Rails.configuration.authbox[:api_key],
    '$secretKey' => Rails.configuration.authbox[:secret_key],
    '$rails' => {
      '$controller' => controller_name,
      '$action' => action_name
    }
  }

  body.merge!(authbox_get_request_data())
  body.merge!(action)

  base_uri = authbox_get_endpoint()
  uri = URI(base_uri + endpoint)

  if async
    Authbox.authbox_pool.process do
      authbox_post_form(uri, body)
    end
    return
  end

  response = authbox_post_form(uri, body)

  begin
    parsed_response = JSON.parse(response.body)
    if not parsed_response.has_key? 'type'
      @authbox_verdict = {'type' => 'ALLOW', 'info' => 'No verdict returned'}
      return @authbox_verdict
    end
  rescue => e
    authbox_warn_log { "Error decoding body: #{e}" }

    # Fail open
    @authbox_verdict = {'type' => 'ALLOW', 'info' => 'Error from server'}
    return @authbox_verdict
  end

  @authbox_verdict = parsed_response
  return @authbox_verdict
end
authbox_warn_log(&message_block) click to toggle source
# File lib/authbox.rb, line 123
def authbox_warn_log(&message_block)
  logger.tagged('AUTHBOX') {
    logger.warn message_block
  }
end