Integration Rails APP With Doorkeeper & Omniauth2

By WinDy

2015.4.18

About Me

Contents

  • Study Case( jiaoluo.it )
  • Methods about Account Integration
  • Omniauth2 in Rails
  • Doorkeeper
  • Define custom strategy
  • SSO Logout
  • Data Transfer

Study Case

Study Case(2)

Study Case(3)

Methods Account System Integration

  • Rewrite Code Together
  • Rails Engine System
  • SSO system
  • Oauth2 !!!

What's the less alteration solution?

Omniauth2

Principle of Omiauth2

  • RO (resource owner)
  • RS (resource server)
  • Client
  • AS (authorization server)

Oauth2 in Rails

  • omniauth
  • omniauth-oauth2
  • omniauth-github
  • oauth2
  • devise-omniauth2
  • doorkeeper
  • omniauth ( rake middleware )
  • omniauth-oauth2 ( abstract interface )
  • omniauth-github ( definite implement )
  • oauth2 ( client )
  • devise-omniauth2 ( devise omniauth2 integration )
  • doorkeeper ( server )

Doorkeeper

An OAuth 2 provider for Rails


# configuration of doorkeeper
Doorkeeper.configure do
  orm :active_record

  resource_owner_authenticator do
    if Doorkeeper::Application.where(uid: params[:client_id]).first.try(:name) == 'forum'
      session[:previous_url] = ENV['FORUM']
    end
    current_user || redirect_to(new_user_session_url)
    current_user
  end

  admin_authenticator do
    current_user && current_user.has_role?(:admin) || redirect_to(root_path)
  end
  skip_authorization do |resource_owner, client|
    client.name == 'forum'
  end
end

# Api basement controller
class Api::ApiController < ApplicationController

  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

# api impelement :  user
class Api::UsersController < Api::ApiController

  before_action :doorkeeper_authorize!
  def show
    @user =  current_resource_owner
  end

end


# json.jbuilder
json.extract! @user, :id, :email, :nickname
json.avatar_url image_path(@user.avatar_url)

# route
Rails.application.routes.draw do
  use_doorkeeper

  namespace :api do
    resource :user, only: [:show]
  end
end

Management

  • http://yourserver/applications
  • create your owner application by user
  • more link

Write custom oauth2 strategy

  • gem:  omniauth
  • Inherited OmniAuth::Strategies::OAuth2
  • call provider's api
  • refactor user model
# a client strategy
module OmniAuth
  module Strategies
    class Jiaoluo < OmniAuth::Strategies::OAuth2
      SITE = CONFIG['JIAOLUO_SITE']
      option :name, "jiaoluo"

      option :client_options, {
        :site => SITE,
        :authorize_url => "#{SITE}/oauth/authorize",
        :token_url => "#{SITE}/oauth/token",
      }

      uid{ raw_info['id'] }

      info do
        {
          :name => raw_info['nickname'],
          :email => raw_info['email'],
          :avatar => raw_info['avatar_url'],
        }
      end

      extra do
        {
          'raw_info' => raw_info
        }
      end

      def raw_info
        @raw_info ||= access_token.get('/api/user').parsed
      end
    end
  end
end

# jiaoluo middleware
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :developer unless Rails.env.production?
  provider :jiaoluo, CONFIG['JIAOLUO_KEY'], CONFIG['JIAOLUO_SECRET']
end

# route
Rails.application.routes.draw do

  get '/auth/:provider/callback', to: 'sessions#create_from_omniauth'

end

# session controller
class SessionsController < ApplicationController

  def create_from_omniauth
    omniauth = request.env["omniauth.auth"]
    @user = User.where(jiaoluo_uid: omniauth['uid'].to_s).first
    if @user
      @user.update_from_omniauth(omniauth)
    else
      @user = User.create_from_omniauth(omniauth)
    end
    login_as @user
    #remember_me
    redirect_back_or_default root_url
  end

end

# user model
class User < ActiveRecord::Base
  def self.create_from_omniauth(omniauth)
    user = User.new(
      username: omniauth['info']['name'],
      jiaoluo_uid: omniauth['uid'],
      name: omniauth['info']['name'],
      email: omniauth['info']['email'],
      avatar: omniauth['info']['avatar']
    )
    user.save!
    user
  end
end

# session logout
class User < ActiveRecord::Base
  def destroy
    logout
    if CONFIG['JIAOLUO_SITE'].present?
      redirect_to CONFIG['JIAOLUO_SITE'] + '/logout'
    else
      redirect_to root_path
    end
  end

end

# config YML

JIAOLUO_KEY: cefcafaf6d7cf962dd610a5ea99be16fb15113ddd1d713a91a12fc76da51d9af
JIAOLUO_SECRET: b8d26c6c9b63199cc31e7e8131a278add3ac345567151f981ad1fac305ad445b
JIAOLUO_SITE: http://jiaoluo.it

Date Transfer

Create Topic synchronously when Jiaoluo create a course

Redis !!!

# redis resque form
class ResqueForum
  # create topic in Jiaoluo forum
  def self.push(course_type, title, body, user)
    return if Rails.env == 'test'
    user = {
      uid: user.id,
      name: user.nickname,
      email: user.email,
      avatar_url: ActionController::Base.helpers.asset_path(user.avatar_url),
    }
    if ENV['REDIS_HOST'].present?
      @@redis ||= Redis.new(host: ENV['REDIS_HOST'],
                         port: ENV['REDIS_PORT'],
                         db: ENV['REDIS_DB'],
                         driver: :hiredis)
      args = {
        class: ENV['REDIS_CLASS_JOB'],
        args: [course_type, title, body, user],
      }.to_json
      queues = ENV['REDIS_QUEUE'].split(':')[0..1].join(':')
      queue_name = ENV['REDIS_QUEUE'].split(':')[-1]
      queues += 's'
      @@redis.sadd(queues, queue_name)
      @@redis.rpush(ENV['REDIS_QUEUE'], args)
    end
  end
end

Dissatisfied Problem

  • SSO logout from master
  • a little dirty for configuration
  • a little slow for user experiences

QA?