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 & 1.9.3 (preferred version)
  • Rails 3.0+ (works with Rails 3.2.*) or Merb

Installation

Add it to the 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
ENV['SSO_COOKIE_DOMAIN']  = "example.com"   # [OPTIONAL] set the domain for which the Signle Sign On cookie will be set (default: exvo.com)
ENV['SSO_COOKIE_SECRET']  = "secret"        # Single Sign On secret key (generate using `SecureRandom.hex(16)`)

But you can also set things directly in the config/application.rb file:

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"
Exvo::Helpers.sso_cookie_domain  = "example.com"
Exvo::Helpers.sso_cookie_secret  = "secret"

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 the ones you want.

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

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" => "PL",
        "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

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

Require authentication in your controllers

If you want to protect your controllers from unauthorized access, you can add in your ApplicationController (for all controllers) or in some controller:

before_filter :authenticate_user!

If your application requires being accessed by a not logged in users, but you would still like to display a "logged in" state for users, which are logged in, you can unobtrusively authenticate such users by adding this in your controller(s):

before_filter :unobtrusively_authenticate_user!

Signle Sign On

All Exvo applications use the Single Sign On mechanism with a shared cookie visible to all apps using the exvo.com domain. This is a signed cookie (HMAC), that cannot be tampered with.

After signing in or signin up Exvo Auth, this cookie is set and from this moment each application will authenticate the user based only on this cookie. Signing out will delete this cookie.

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.

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