*Not affiliated with McDonalds Corp in any way.
https://github.com/jagthedrummer/so_auth_provider
https://github.com/jagthedrummer/so_auth
https://github.com/jagthedrummer/so_auth_client
Just a web client
GET /protected
Redirect #{provider}/authorize?client_id=123&state=yyz&response_type=code
GET #{provider}/authorize?client_id=123&state=yyz&response_type=code
Redirect #{consumer}/callback?code=abc&state=yyz&response_type=code
GET #{consumer}/callback?code=abc&state=yyz&response_type=code
POST /oauth/token?client_id=123&client_secret=456&code=abc
{token:'42ab', refresh_token:'9876', uid:'jag'}
GET /me.json?token=42ab
{id:111, name:"Jeremy Green", email:"jeremy@octolabs.com"}
Redirect /protected
GET /protected
200 OK /protected Whew!
GET /protected
Redirect #{provider}/...
  $ rails plugin new so_auth \
      --full \
      -T \
      --dummy-path=spec/dummy
  Gem::Specification.new do |s|
    #...
    s.add_dependency 'omniauth', "~> 1.2.1"
    s.add_dependency 'omniauth-oauth2', "~> 1.1.2"
    #...
  end
  # lib/omniauth/strategies/so.rb
  require 'omniauth-oauth2'
  class OmniAuth::Strategies::So < OmniAuth::Strategies::OAuth2
    option :name, 'so'
    option :client_options, {
      :site =>  ENV['AUTH_PROVIDER_URL'],
      :authorize_path => '/oauth/authorize',
      :access_token_path => '/oauth/token'
    }
  end
  # spec/dummy/config/initializers/omniauth.rb
  # If you need to pull the app_id and app_secret from a different spot
  # this is the place to do it
  APP_ID = ENV['AUTH_PROVIDER_APPLICATION_ID'] || "not_a_real_id"
  APP_SECRET = ENV['AUTH_PROVIDER_SECRET'] || "not_a_real_secret"
  Rails.application.config.middleware.use OmniAuth::Builder do
    provider :so, APP_ID, APP_SECRET
  end
  spec/dummy $ rails g controller stuff private public
      create  app/controllers/stuff_controller.rb
       route  get "stuff/public"
       route  get "stuff/private"
      invoke  erb
      create    app/views/stuff
      create    app/views/stuff/private.html.erb
      create    app/views/stuff/public.html.erb
  # spec/dummy/app/controllers/stuff_controller.rb
  class StuffController < ApplicationController
    before_filter :login_required, :only => [:private]
  end
  # spec/dummy/app/controllers/application_controller.rb
  class ApplicationController < SoAuth::ApplicationController
    #...
  end
  class SoAuth::ApplicationController < ActionController::Base
    
    def login_required
      not_authorized
    end
    def not_authorized
      respond_to do |format|
        format.html{
          redirect_to "/auth/so?origin=#{request.original_url}"
        }
        format.json{ head :unauthorized }
      end
    end
  end
GET #{provider}/auth...
Redirect#{consumer}/callback...
# Gemfile gem 'doorkeeper', '~> 1.1.0'
$ bundle install $ rails generate doorkeeper:install $ rails generate doorkeeper:migration $ rake db:migrate
  # config/initializers/doorkeeper.rb
  Doorkeeper.configure do
    # This should either return the current user
    # or redirect to the sign in page.
    resource_owner_authenticator do
      current_user || warden.authenticate!(:scope => :user)
    end
    #...
  end
  # config/initializers/doorkeeper.rb
  Doorkeeper.configure do
    # We want to skip the screen that asks
    # "can application X use your profile?"
    skip_authorization do |resource_owner, client|
      true
    end
    #...
  end
GET #{provider}/auth...
doorkeeperRedirect
GET #{consumer}/callback...
omniauthPOST /oauth/token...
doorkeeper{token:...}
omniauth/so_authGET /me.json...
???{name:...}
  # config/routes.rb
  SoAuthProvider::Application.routes.draw do
    
    #...
    get "oauth/me" => "oauth/users#me"
    #...
  end
  # oauth/users_controller.rb
  class Oauth::UsersController < ApplicationController
    doorkeeper_for :all # sets doorkeeper_token or sends 401 error
    respond_to     :json
    # GET /me.json
    def me
      respond_with current_resource_owner
    end
    private
    # Find the user that owns the access token
    def current_resource_owner
      User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
    end
  end
Provider App{name:...}
??? Redirect /protected
  # lib/omniauth/strategies/so.rb
  class OmniAuth::Strategies::So < OmniAuth::Strategies::OAuth2
    #...
    def raw_user_info
      @raw_user_info ||= access_token.get('/oauth/me').parsed
    end
    uid { raw_user_info['id'] }
  end
  # lib/omniauth/strategies/so.rb
  class OmniAuth::Strategies::So < OmniAuth::Strategies::OAuth2
    #...
    info do
      {
        :email => raw_user_info['email'],
        :admin => raw_user_info['admin']
      }
    end
    extra do
      { :raw_user_info => raw_user_info }
    end
  end
  # config/routes.rb
  Rails.application.routes.draw do
    # ...
    # omniauth callbacks
    get '/auth/:provider/callback', :to => 'so_auth/user_sessions#create'
    get '/auth/failure', :to => 'so_auth/user_sessions#failure'
  end
  class SoAuth::UserSessionsController < SoAuth::ApplicationController
    # omniauth callback method
    def create
      omniauth = env['omniauth.auth']
      user = User.find_or_create_by(:id => omniauth['uid'])
      if user.respond_to?(:email)
        user.email = omniauth['info']['email']
      end
      user.save
      session[:user_id] = user.id
      redirect_to request.env['omniauth.origin'] || root_path
    end
    #...
  end
  class SoAuth::UserSessionsController < SoAuth::ApplicationController
    #...
    # Omniauth failure callback
    def failure
      flash[:notice] = params[:message]
      redirect_to root_path
    end
  end
  class SoAuth::ApplicationController < ActionController::Base
    #...
    def current_user
      return nil unless session[:user_id]
      @current_user ||= User.find_by_id(session[:user_id])
    end
    def signed_in?
      current_user.present?
    end
    helper_method :signed_in?
    helper_method :current_user
  end
  class SoAuth::ApplicationController < ActionController::Base
    def login_required
      not_authorized unless current_user
    end
    #...
  end
omniauth & so_auth Redirect /protected
  class Oauth::SessionsController < Devise::SessionsController
    def set_user_cookie(user)
      cookies[:so_auth] = { :value => user.id, 
                            :domain => env_domain }
    end
    def remove_user_cookie
      cookies.delete(:so_auth, :domain => env_domain)
    end
    def env_domain
      domain = ENV['COOKIE_DOMAIN'] || "localhost"
      domain == "localhost" ? :all : domain
    end
    #...
  end
  # config/routes.rb
  SoAuthProvider::Application.routes.draw do
    
    #...
    
    devise_for :users, 
      :controllers => {:sessions => "oauth/sessions"}
    #...
  end
  class Oauth::SessionsController < Devise::SessionsController
    def create
      super {|resource| set_user_cookie(resource) }
    end
    def destroy
      super {|resource| remove_user_cookie }
    end
    #...
  end
  class SoAuth::ApplicationController < ActionController::Base
    
    before_filter :check_cookie
    def check_cookie
      reset_session unless cookie_valid?
    end
    def cookie_valid?
      cookies[:so_auth].present? && 
        session[:user_id].present? && 
        cookies[:so_auth].to_s == session[:user_id].to_s
    end
    
    #...
  end
  # config/routes.rb
  Rails.application.routes.draw do
    # Custom logout
    post '/logout', :to => 'so_auth/user_sessions#destroy'
  end
  class SoAuth::UserSessionsController < SoAuth::ApplicationController
    #...
    # logout - Clear our rack session BUT importantly redirect
    # to the provider to clean up the there too!
    def destroy
      reset_session
      redirect_to "#{ENV['AUTH_PROVIDER_URL']}/users/sign_out"
    end
  end
$ rails new so_auth_consumer -T --database=postgresql
# Gemfile # development gem 'so_auth', path: "../so_auth"
# for deployment gem 'vendorise', group: :development # rake "vendorise:gem[git@github.com:jagthedrummer/so_auth.git]" gem 'so_auth', path: "vendor/gems/so_auth"
  $ rails generate so_auth:install
      create  config/initializers/omniauth.rb
  # If you need to pull the app_id and app_secret from a different spot
  # this is the place to do it
  APP_ID = ENV['AUTH_PROVIDER_APPLICATION_ID'] || "not_a_real_id"
  APP_SECRET = ENV['AUTH_PROVIDER_SECRET'] || "not_a_real_secret"
  Rails.application.config.middleware.use OmniAuth::Builder do
    provider :so, APP_ID, APP_SECRET
  end
AUTH_PROVIDER_URL=http://localhost:3000 AUTH_PROVIDER_APPLICATION_ID=1234 AUTH_PROVIDER_SECRET=5678
$ rails generate model user email:string $ rake db:migrate; rake db:test:prepare
  # app/controllers/some_controller.rb
  class SomeController < ApplicationController
    before_filter :login_required, :only => [:private_stuff]
  end
@jagthedrummer
jeremy@octolabs.com