mirror of
https://github.com/omniauth/omniauth.git
synced 2022-11-09 12:31:49 -05:00
OAuth, Twitter, LinkedIn, and OpenID strategies are all up and running.
This commit is contained in:
parent
87e171038e
commit
a9ef69754d
13 changed files with 234 additions and 55 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,6 +2,7 @@
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
/live
|
/live
|
||||||
|
.rvmrc
|
||||||
|
|
||||||
## TEXTMATE
|
## TEXTMATE
|
||||||
*.tmproj
|
*.tmproj
|
||||||
|
|
2
Rakefile
2
Rakefile
|
@ -13,6 +13,8 @@ begin
|
||||||
gem.add_dependency 'rack'
|
gem.add_dependency 'rack'
|
||||||
gem.add_dependency 'rest-client'
|
gem.add_dependency 'rest-client'
|
||||||
gem.add_dependency 'oauth'
|
gem.add_dependency 'oauth'
|
||||||
|
gem.add_dependency 'nokogiri'
|
||||||
|
gem.add_dependency 'json'
|
||||||
gem.add_development_dependency "rspec", ">= 1.2.9"
|
gem.add_development_dependency "rspec", ">= 1.2.9"
|
||||||
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,6 +24,10 @@ module OmniAuth
|
||||||
attr_accessor :path_prefix
|
attr_accessor :path_prefix
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.build(stack, &block)
|
||||||
|
OmniAuth::Builder.new(stack, &block)
|
||||||
|
end
|
||||||
|
|
||||||
def self.config
|
def self.config
|
||||||
Configuration.instance
|
Configuration.instance
|
||||||
end
|
end
|
||||||
|
@ -31,9 +35,40 @@ module OmniAuth
|
||||||
def self.configure
|
def self.configure
|
||||||
yield config
|
yield config
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module Utils
|
||||||
|
extend self
|
||||||
|
|
||||||
|
def deep_merge(hash, other_hash)
|
||||||
|
target = hash.dup
|
||||||
|
|
||||||
|
other_hash.keys.each do |key|
|
||||||
|
if other_hash[key].is_a? ::Hash and hash[key].is_a? ::Hash
|
||||||
|
target[key] = deep_merge(target[key],other_hash[key])
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
target[key] = other_hash[key]
|
||||||
|
end
|
||||||
|
|
||||||
|
target
|
||||||
|
end
|
||||||
|
|
||||||
|
def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
|
||||||
|
return "OAuth" if lower_case_and_underscored_word.to_s == 'oauth'
|
||||||
|
return "OpenID" if lower_case_and_underscored_word.to_s == 'open_id'
|
||||||
|
|
||||||
|
if first_letter_in_uppercase
|
||||||
|
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
||||||
|
else
|
||||||
|
lower_case_and_underscored_word.first + camelize(lower_case_and_underscored_word)[1..-1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
require 'omni_auth/strategy'
|
require 'omni_auth/strategy'
|
||||||
%w(oauth http_basic linked_in gowalla twitter).each do |s|
|
%w(oauth http_basic linked_in gowalla twitter open_id).each do |s|
|
||||||
require "omni_auth/strategies/#{s}"
|
require "omni_auth/strategies/#{s}"
|
||||||
end
|
end
|
||||||
|
require 'omni_auth/builder'
|
23
lib/omni_auth/builder.rb
Normal file
23
lib/omni_auth/builder.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
module OmniAuth
|
||||||
|
class Builder < Rack::Builder
|
||||||
|
def initialize(app, &block)
|
||||||
|
@app = app
|
||||||
|
super(&block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def provider(klass, *args, &block)
|
||||||
|
if klass.is_a?(Class)
|
||||||
|
middleware = klass
|
||||||
|
else
|
||||||
|
middleware = OmniAuth::Strategies.const_get("#{OmniAuth::Utils.camelize(klass.to_s)}")
|
||||||
|
end
|
||||||
|
|
||||||
|
use middleware, *args, &block
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
@ins << @app unless @ins.include?(@app)
|
||||||
|
to_app.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,16 +2,16 @@
|
||||||
# so this won't actually work at all it
|
# so this won't actually work at all it
|
||||||
# turns out.
|
# turns out.
|
||||||
|
|
||||||
module OmniAuth
|
# module OmniAuth
|
||||||
module Strategies
|
# module Strategies
|
||||||
class Gowalla < OmniAuth::Strategies::HttpBasic #:nodoc:
|
# class Gowalla < OmniAuth::Strategies::HttpBasic #:nodoc:
|
||||||
def initialize(app, api_key)
|
# def initialize(app, api_key)
|
||||||
super(app, :gowalla, nil, {'X-Gowalla-API-Key' => api_key, 'Accept' => 'application/json'})
|
# super(app, :gowalla, nil, {'X-Gowalla-API-Key' => api_key, 'Accept' => 'application/json'})
|
||||||
end
|
# end
|
||||||
|
#
|
||||||
def endpoint
|
# def endpoint
|
||||||
"http://#{request[:username]}:#{request[:password]}@api.gowalla.com/users/#{request[:username]}"
|
# "http://#{request[:username]}:#{request[:password]}@api.gowalla.com/users/#{request[:username]}"
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
end
|
# end
|
|
@ -14,13 +14,18 @@ module OmniAuth
|
||||||
attr_reader :endpoint, :request_headers
|
attr_reader :endpoint, :request_headers
|
||||||
|
|
||||||
def request_phase
|
def request_phase
|
||||||
resp = RestClient.get(endpoint, request_headers)
|
@response = RestClient.get(endpoint, request_headers)
|
||||||
|
request.POST['auth'] = auth_hash
|
||||||
|
@env['HTTP_METHOD'] = 'GET'
|
||||||
|
@env['PATH_INFO'] = "#{OmniAuth.config.path_prefix}/#{name}/callback"
|
||||||
|
|
||||||
|
@app.call(@env)
|
||||||
rescue RestClient::Request::Unauthorized
|
rescue RestClient::Request::Unauthorized
|
||||||
fail!(:invalid_credentials)
|
fail!(:invalid_credentials)
|
||||||
end
|
end
|
||||||
|
|
||||||
def callback_phase
|
def callback_phase
|
||||||
|
[401, {}, 'Unauthorized']
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
require 'nokogiri'
|
||||||
|
|
||||||
module OmniAuth
|
module OmniAuth
|
||||||
module Strategies
|
module Strategies
|
||||||
class LinkedIn < OmniAuth::Strategies::OAuth
|
class LinkedIn < OmniAuth::Strategies::OAuth
|
||||||
|
@ -9,6 +11,35 @@ module OmniAuth
|
||||||
:authorize_path => '/uas/oauth/authorize',
|
:authorize_path => '/uas/oauth/authorize',
|
||||||
:scheme => :header)
|
:scheme => :header)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def auth_hash
|
||||||
|
hash = user_hash(@access_token)
|
||||||
|
|
||||||
|
OmniAuth::Utils.deep_merge(super, {
|
||||||
|
'uid' => hash.delete('id'),
|
||||||
|
'user_info' => hash
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_hash(access_token)
|
||||||
|
person = Nokogiri::XML::Document.parse(@access_token.get('/v1/people/~:(id,first-name,last-name,headline,member-url-resources,picture-url,location)').body).xpath('person')
|
||||||
|
|
||||||
|
hash = {
|
||||||
|
'id' => person.xpath('id').text,
|
||||||
|
'first_name' => person.xpath('first-name').text,
|
||||||
|
'last_name' => person.xpath('last-name').text,
|
||||||
|
'location' => person.xpath('location/name').text,
|
||||||
|
'image' => person.xpath('picture-url').text,
|
||||||
|
'description' => person.xpath('headline').text,
|
||||||
|
'urls' => person.css('member-url-resources member-url').inject({}) do |hash,element|
|
||||||
|
hash[element.xpath('name').text] = element.xpath('url').text
|
||||||
|
hash
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
hash[:name] = "#{hash['first_name']} #{hash['last_name']}"
|
||||||
|
hash
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -22,31 +22,27 @@ module OmniAuth
|
||||||
request_token = ::OAuth::RequestToken.new(consumer, session[:oauth][name.to_sym].delete(:request_token), session[:oauth][name.to_sym].delete(:request_secret))
|
request_token = ::OAuth::RequestToken.new(consumer, session[:oauth][name.to_sym].delete(:request_token), session[:oauth][name.to_sym].delete(:request_secret))
|
||||||
@access_token = request_token.get_access_token(:oauth_verifier => request.params['oauth_verifier'])
|
@access_token = request_token.get_access_token(:oauth_verifier => request.params['oauth_verifier'])
|
||||||
|
|
||||||
request[:auth] = {
|
request['auth'] = self.auth_hash
|
||||||
:provider => name.to_sym,
|
|
||||||
:uid => unique_id,
|
|
||||||
:credentials => {
|
|
||||||
:token => @access_token.token,
|
|
||||||
:secret => @access_token.secret
|
|
||||||
}, :extra => {
|
|
||||||
:access_token => @access_token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@app.call(self.env)
|
@app.call(self.env)
|
||||||
rescue ::OAuth::Unauthorized
|
rescue ::OAuth::Unauthorized
|
||||||
fail!(:invalid_credentials)
|
fail!(:invalid_credentials)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def auth_hash
|
||||||
|
OmniAuth::Utils.deep_merge(super, {
|
||||||
|
'credentials' => {
|
||||||
|
'token' => @access_token.token,
|
||||||
|
'secret' => @access_token.secret
|
||||||
|
}, 'extra' => {
|
||||||
|
'access_token' => @access_token
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
def unique_id
|
def unique_id
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def full_host
|
|
||||||
uri = URI.parse(request.url)
|
|
||||||
uri.path = ''
|
|
||||||
uri.to_s
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
63
lib/omni_auth/strategies/open_id.rb
Normal file
63
lib/omni_auth/strategies/open_id.rb
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
require 'rack/openid'
|
||||||
|
|
||||||
|
module OmniAuth
|
||||||
|
module Strategies
|
||||||
|
class OpenID
|
||||||
|
include OmniAuth::Strategy
|
||||||
|
|
||||||
|
def initialize(app, store = nil, options = {})
|
||||||
|
super(app, :open_id)
|
||||||
|
@options = options
|
||||||
|
@options[:required] ||= %w(email fullname)
|
||||||
|
@options[:optional] ||= %w(nickname dob gender postcode country language timezone)
|
||||||
|
@store = store
|
||||||
|
end
|
||||||
|
|
||||||
|
def dummy_app
|
||||||
|
lambda{|env| [401, {"WWW-Authenticate" => Rack::OpenID.build_header(
|
||||||
|
:identifier => request[:identifier],
|
||||||
|
:return_to => request.url + '/callback',
|
||||||
|
:required => @options[:required],
|
||||||
|
:optional => @options[:optional]
|
||||||
|
)}, []]}
|
||||||
|
end
|
||||||
|
|
||||||
|
def request_phase
|
||||||
|
return fail!(:missing_information) unless request[:identifier]
|
||||||
|
openid = Rack::OpenID.new(dummy_app, @store)
|
||||||
|
openid.call(env)
|
||||||
|
end
|
||||||
|
|
||||||
|
def callback_phase
|
||||||
|
openid = Rack::OpenID.new(lambda{|env| [200,{},[]]}, @store)
|
||||||
|
openid.call(env)
|
||||||
|
resp = env.delete('rack.openid.response')
|
||||||
|
|
||||||
|
case resp.status
|
||||||
|
when :failure
|
||||||
|
fail!(:invalid_credentials)
|
||||||
|
when :success
|
||||||
|
request['auth'] = auth_hash(resp)
|
||||||
|
@app.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def auth_hash(response)
|
||||||
|
{
|
||||||
|
'uid' => response.display_identifier,
|
||||||
|
'user_info' => user_info(response.display_identifier, ::OpenID::SReg::Response.from_success_response(response))
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_info(identifier, sreg)
|
||||||
|
{
|
||||||
|
'email' => sreg['email'],
|
||||||
|
'name' => sreg['fullname'],
|
||||||
|
'location' => sreg['postcode'],
|
||||||
|
'nickname' => sreg['nickname'],
|
||||||
|
'urls' => {'Profile' => identifier}
|
||||||
|
}.reject{|k,v| v.nil? || v == ''}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -15,22 +15,31 @@ module OmniAuth
|
||||||
:site => 'https://api.twitter.com')
|
:site => 'https://api.twitter.com')
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_hash
|
def auth_hash
|
||||||
@user_hash ||= JSON.parse(@access_token.get('/1/account/verify_credentials.json'), :symbolize_keys => true)
|
OmniAuth::Utils.deep_merge(super, {
|
||||||
end
|
'uid' => @access_token.params[:user_id],
|
||||||
|
'user_info' => user_info,
|
||||||
def unique_id
|
'extra' => {'user_hash' => user_hash}
|
||||||
@access_token.params[:user_id]
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_info
|
def user_info
|
||||||
|
user_hash = self.user_hash
|
||||||
|
|
||||||
{
|
{
|
||||||
:name => user_hash[:name],
|
'nickname' => user_hash['screen_name'],
|
||||||
:image => user_hash[:profile_image_url],
|
'name' => user_hash['name'],
|
||||||
:screen_name => user_hash[:screen_name],
|
'location' => user_hash['location'],
|
||||||
:description => user_hash[:description]
|
'image' => user_hash['profile_image_url'],
|
||||||
|
'screen_name' => user_hash['screen_name'],
|
||||||
|
'description' => user_hash['description'],
|
||||||
|
'urls' => {'Website' => user_hash['url']}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def user_hash
|
||||||
|
@user_hash ||= JSON.parse(@access_token.get('/1/account/verify_credentials.json').body)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -34,6 +34,19 @@ module OmniAuth
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def auth_hash
|
||||||
|
{
|
||||||
|
'provider' => name.to_s,
|
||||||
|
'uid' => nil
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def full_host
|
||||||
|
uri = URI.parse(request.url)
|
||||||
|
uri.path = ''
|
||||||
|
uri.to_s
|
||||||
|
end
|
||||||
|
|
||||||
def session
|
def session
|
||||||
@env['rack.session']
|
@env['rack.session']
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
||||||
require 'auth_elsewhere'
|
|
||||||
|
|
||||||
class Rack::Session::Phony
|
class Rack::Session::Phony
|
||||||
def initialize(app); @app = app end
|
def initialize(app); @app = app end
|
||||||
|
@ -12,9 +11,11 @@ end
|
||||||
def app
|
def app
|
||||||
Rack::Builder.new {
|
Rack::Builder.new {
|
||||||
use Rack::Session::Phony
|
use Rack::Session::Phony
|
||||||
use AuthElsewhere::OAuth, :twitter, 'abc', 'def', :site => 'https://api.twitter.com'
|
use OmniAuth::Builder do
|
||||||
use AuthElsewhere::OAuth, :linked_in, 'abc', 'def', :site => 'https://api.linkedin.com'
|
provider :oauth, :twitter, 'abc', 'def', :site => 'https://api.twitter.com'
|
||||||
run lambda { |env| [200, {'Content-Type' => 'text/plain'}, env.key?(:oauth).to_s] }
|
provider :oauth, :linked_in, 'abc', 'def', :site => 'https://api.linkedin.com'
|
||||||
|
end
|
||||||
|
run lambda { |env| [200, {'Content-Type' => 'text/plain'}, Rack::Request.new(env).params.key?('auth').to_s] }
|
||||||
}.to_app
|
}.to_app
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ def session
|
||||||
last_request.env['rack.session']
|
last_request.env['rack.session']
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "AuthElsewhere::OAuth" do
|
describe "OmniAuth::Strategies::OAuth" do
|
||||||
before do
|
before do
|
||||||
stub_request(:post, 'https://api.twitter.com/oauth/request_token').
|
stub_request(:post, 'https://api.twitter.com/oauth/request_token').
|
||||||
to_return(:body => "oauth_token=yourtoken&oauth_token_secret=yoursecret&oauth_callback_confirmed=true")
|
to_return(:body => "oauth_token=yourtoken&oauth_token_secret=yoursecret&oauth_callback_confirmed=true")
|
||||||
|
@ -50,8 +51,8 @@ describe "AuthElsewhere::OAuth" do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should exchange the request token for an access token' do
|
it 'should exchange the request token for an access token' do
|
||||||
last_request.env[:oauth][:provider].should == :twitter
|
last_request['auth']['provider'].should == 'twitter'
|
||||||
last_request.env[:oauth][:access_token].should be_kind_of(OAuth::AccessToken)
|
last_request['auth']['extra']['access_token'].should be_kind_of(OAuth::AccessToken)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should call through to the master app' do
|
it 'should call through to the master app' do
|
||||||
|
@ -60,12 +61,12 @@ describe "AuthElsewhere::OAuth" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'AuthElsewhere::Twitter' do
|
describe 'OmniAuth::Strategies::Twitter' do
|
||||||
it 'should subclass OAuth' do
|
it 'should subclass OAuth' do
|
||||||
AuthElsewhere::Twitter.should < AuthElsewhere::OAuth
|
OmniAuth::Strategies::Twitter.should < OmniAuth::Strategies::OAuth
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should initialize with just consumer key and secret' do
|
it 'should initialize with just consumer key and secret' do
|
||||||
AuthElsewhere::Twitter.new({},'abc','def')
|
OmniAuth::Strategies::Twitter.new({},'abc','def')
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -2,7 +2,7 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
|
||||||
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
||||||
|
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
require 'auth_elsewhere'
|
require 'omni_auth'
|
||||||
require 'spec'
|
require 'spec'
|
||||||
require 'spec/autorun'
|
require 'spec/autorun'
|
||||||
require 'rack/test'
|
require 'rack/test'
|
||||||
|
|
Loading…
Add table
Reference in a new issue