exvo_auth

This gem supplements the omniauth-exvo gem. Together they implement the oauth2 protocol for handling users and applications authentication at Exvo.

This gem depends on the exvo_helpers gem for all of its configuration.

Note, that this gem was previously named "exvo-auth".

Requirements

  • Runs on Ruby 1.8.7 & 1.9.2 (preferred version)
  • Rails 3.0+ (works with Rails 3.1) or Merb

Installation

Add it to Gemfile:

gem "exvo_auth"

Run bundle:

$ bundle

The preferred way to configure this gem is via the ENV variables:

ENV['AUTH_CLIENT_ID']     = "foo"
ENV['AUTH_CLIENT_SECRET'] = "bar"
ENV['AUTH_DEBUG']         = "true"          # [OPTIONAL] dumps all HTTP traffic to STDERR, useful during development; it *has to be a string, not a boolean*
ENV['AUTH_REQUIRE_SSL']   = "false"         # [OPTIONAL] disable SSL, useful in development (note that all apps API urls must be http, not https); it *has to be a string, not a boolean*
ENV['AUTH_HOST']          = "test.exvo.com" # [OPTIONAL] override the default auth host

But you can also set things directly in the config/application.rb file (before the middleware declaration):

Exvo::Helpers.auth_client_id     = "foo"
Exvo::Helpers.auth_client_secret = "bar"
Exvo::Helpers.auth_debug         = true            # boolean
Exvo::Helpers.auth_require_ssl   = false           # boolean
Exvo::Helpers.auth_host          = "test.exvo.com"

Add this line to config/application.rb:

config.middleware.use ExvoAuth::Middleware

Add routes (Rails example):

match "/auth/exvo/callback" => "sessions#create"
match "/auth/failure"       => "sessions#failure"
match "/sign_out"           => "sessions#destroy"

Include controller helpers in your ApplicationController.rb:

include ExvoAuth::Controllers::Rails # (or Merb)

Implement a sessions controller (Rails example):

class SessionsController < ApplicationController
  def create
    
  end

  def destroy
    sign_out_and_redirect!
  end

  def failure
    render :text => "Sorry!"
  end
end

It's good to have your SessionsController's #create action a little more extended, so that each time the user logs in into the app, his user data (like email, nickname, etc.) is updated from the auth app (i.e. from his profile):

def create
  auth_hash = request.env["omniauth.auth"]
  user = User.find_or_create_by_uid(auth_hash["uid"])

  if user && user.update_from_auth_hash(auth_hash)
    
  else
    fail "Could not update user"
  end
end

and your #update_from_auth_hash method at User model might look like this:

def update_from_auth_hash(auth_hash)
  update_attributes({
    :nickname => auth_hash["info"]["nickname"],
    :email => auth_hash["info"]["email"],
    :plan => auth_hash["extra"]["raw_info"]["plan"],
    :language => auth_hash["extra"]["raw_info"]["language"]
  })
end

Note, that you don't have to update all user attributes. You can update only ones you want.

This is what you get (and what you can use/save for the local user) from auth (example data as of 2012-01):

request.env["omniauth.auth"].to_hash.inspect

  {
    "provider" => "exvo",
    "uid" => 1,

    "credentials" => {
      "token" => "a2d09701559b9f26a8284d6f94670477d882ad6d9f3d92ce9917262a6b54085fa3fb99e111340459",
      "expires" => false
    },

    "info" => {
      "nickname" => "Pawel",
      "email" => "[email protected]",
      "name" => "Pawel"
    },

    "extra" => {
      "raw_info" => {
        "id" => 1,
        "nickname" => "Pawel",
        "country_code" => nil,
        "plan" => "basic",
        "language" => "en",
        "email" => "[email protected]",
        "referring_user_id" => nil
      }
    }
  }

Implement #find_or_create_user_by_uid(uid) in your ApplicationController:

This method will be called by #current_user. Previously we did this in sessions_controller but since the sharing sessions changes this controller will not be used in most cases because the session comes from another app through a shared cookie. This method should find user by uid or create it.

Exemplary implementation (Rails):

def find_or_create_user_by_uid(uid)
  User.find_or_create_by_uid(uid)
end

It's best to leave this method as it is (without updating any user data inside this method, better to do this in the SessionsController#create action). Updating user in this method might lead to some very hard to debug cyclic executions possibly leading to stack-level too deep errors and/or general slowness, so please proceed with extreme caution.

sign in path:                       "/auth/exvo"
sign up path:                       "/auth/exvo?x_sign_up=true" # this is OAuth2 custom param
sign in path with a return address: "/auth/exvo?state=url"      # using OAuth2 state param

You have a handy methods available in controllers (and views in Rails): sign_in_path and sign_up_path.

Require authentication in your controllers

In ApplicationController (for all controllers) or in some controller just add:

before_filter :authenticate_user!

Fetching user information

All info about any particular user can be obtained using auth api (/users/uid.json path).

Inter-Application Communication

You need to have "App Authorization" created by Exvo first.

Contact us and provide following details:

  • consumer_id - Id of an app that will be a consumer (this is you)
  • provider_id - Id of the provider app
  • scope - The tag associated with the api you want to use in the provider app

Consumer side

consumer = ExvoAuth::Autonomous::Consumer.new(
  :app_id => "this is client_id of the app you want to connect to"
)
consumer.get(*args) # interface is exactly the same like in HTTParty. All http methods are available (post, put, delete, head, options).

Provider side

See #authenticate_app_in_scope!(scope) method in ExvoAuth::Controllers::Rails (or Merb). This method lets you create a before filter. Scopes are used by providing app to check if a given consuming app should have access to a given resource inside a scope. If scopes are empty, then provider app should not present any resources to consumer.

Example of the before filter for provider controller:

before_filter { |c| c.authenticate_app_in_scope!("payments") }

In the provider controller, which is just a fancy name for API controller, you can use #current_app_id method to get the app_id of the app connecting.

Dejavu

Replay non-GET requests after authentication redirects.

Limitations:

  • doesn't work with file uploads
  • all request params become query params when replayed

Copyright © 2011-2012 Exvo.com Development BV, released under the MIT license