mirror of
https://github.com/omniauth/omniauth.git
synced 2022-11-09 12:31:49 -05:00
Initial import.
This commit is contained in:
parent
5a0c7f75f5
commit
795e98266b
14 changed files with 322 additions and 8 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,6 +1,8 @@
|
||||||
## MAC OS
|
## MAC OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
/live
|
||||||
|
|
||||||
## TEXTMATE
|
## TEXTMATE
|
||||||
*.tmproj
|
*.tmproj
|
||||||
tmtags
|
tmtags
|
||||||
|
|
2
Rakefile
2
Rakefile
|
@ -10,6 +10,8 @@ begin
|
||||||
gem.email = "michael@intridea.com"
|
gem.email = "michael@intridea.com"
|
||||||
gem.homepage = "http://github.com/mbleigh/rack-oauthable"
|
gem.homepage = "http://github.com/mbleigh/rack-oauthable"
|
||||||
gem.authors = ["Michael Bleigh"]
|
gem.authors = ["Michael Bleigh"]
|
||||||
|
gem.add_dependency 'rack'
|
||||||
|
gem.add_dependency 'rest-client'
|
||||||
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
|
||||||
|
|
39
lib/omni_auth.rb
Normal file
39
lib/omni_auth.rb
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
require 'rack'
|
||||||
|
require 'singleton'
|
||||||
|
|
||||||
|
module OmniAuth
|
||||||
|
class Configuration
|
||||||
|
include Singleton
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@path_prefix = '/auth'
|
||||||
|
@on_failure = Proc.new do |env, message_key|
|
||||||
|
new_path = "#{OmniAuth.config.path_prefix}/failure?message=#{message_key}"
|
||||||
|
[302, {'Location' => "#{new_path}"}, []]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_failure(&block)
|
||||||
|
if block_given?
|
||||||
|
@on_failure = block
|
||||||
|
else
|
||||||
|
@on_failure
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_accessor :path_prefix
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.config
|
||||||
|
Configuration.instance
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.configure
|
||||||
|
yield config
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
require 'omni_auth/strategy'
|
||||||
|
%w(oauth http_basic linked_in gowalla twitter).each do |s|
|
||||||
|
require "omni_auth/strategies/#{s}"
|
||||||
|
end
|
17
lib/omni_auth/strategies/gowalla.rb
Normal file
17
lib/omni_auth/strategies/gowalla.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Gowalla's API isn't authenticated yet
|
||||||
|
# so this won't actually work at all it
|
||||||
|
# turns out.
|
||||||
|
|
||||||
|
module OmniAuth
|
||||||
|
module Strategies
|
||||||
|
class Gowalla < OmniAuth::Strategies::HttpBasic #:nodoc:
|
||||||
|
def initialize(app, api_key)
|
||||||
|
super(app, :gowalla, nil, {'X-Gowalla-API-Key' => api_key, 'Accept' => 'application/json'})
|
||||||
|
end
|
||||||
|
|
||||||
|
def endpoint
|
||||||
|
"http://#{request[:username]}:#{request[:password]}@api.gowalla.com/users/#{request[:username]}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
28
lib/omni_auth/strategies/http_basic.rb
Normal file
28
lib/omni_auth/strategies/http_basic.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
require 'restclient'
|
||||||
|
|
||||||
|
module OmniAuth
|
||||||
|
module Strategies
|
||||||
|
class HttpBasic
|
||||||
|
include OmniAuth::Strategy
|
||||||
|
|
||||||
|
def initialize(app, name, endpoint, headers = {})
|
||||||
|
super
|
||||||
|
@endpoint = endpoint
|
||||||
|
@request_headers = headers
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :endpoint, :request_headers
|
||||||
|
|
||||||
|
def request_phase
|
||||||
|
resp = RestClient.get(endpoint, request_headers)
|
||||||
|
rescue RestClient::Request::Unauthorized
|
||||||
|
fail!(:invalid_credentials)
|
||||||
|
end
|
||||||
|
|
||||||
|
def callback_phase
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
14
lib/omni_auth/strategies/linked_in.rb
Normal file
14
lib/omni_auth/strategies/linked_in.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
module OmniAuth
|
||||||
|
module Strategies
|
||||||
|
class LinkedIn < OmniAuth::Strategies::OAuth
|
||||||
|
def initialize(app, consumer_key, consumer_secret)
|
||||||
|
super(app, :linked_in, consumer_key, consumer_secret,
|
||||||
|
:site => 'https://api.linkedin.com',
|
||||||
|
:request_token_path => '/uas/oauth/requestToken',
|
||||||
|
:access_token_path => '/uas/oauth/accessToken',
|
||||||
|
:authorize_path => '/uas/oauth/authorize',
|
||||||
|
:scheme => :header)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
52
lib/omni_auth/strategies/oauth.rb
Normal file
52
lib/omni_auth/strategies/oauth.rb
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
module OmniAuth
|
||||||
|
module Strategies
|
||||||
|
class OAuth
|
||||||
|
include OmniAuth::Strategy
|
||||||
|
|
||||||
|
def initialize(app, name, consumer_key, consumer_secret, options = {})
|
||||||
|
require 'oauth'
|
||||||
|
super
|
||||||
|
@consumer = ::OAuth::Consumer.new(consumer_key, consumer_secret, options)
|
||||||
|
end
|
||||||
|
attr_reader :name, :consumer
|
||||||
|
|
||||||
|
def request_phase
|
||||||
|
request_token = consumer.get_request_token(:oauth_callback => full_host + "#{OmniAuth.config.path_prefix}/#{name}/callback")
|
||||||
|
(session[:oauth]||={})[name.to_sym] = {:callback_confirmed => request_token.callback_confirmed?, :request_token => request_token.token, :request_secret => request_token.secret}
|
||||||
|
r = Rack::Response.new
|
||||||
|
r.redirect request_token.authorize_url
|
||||||
|
r.finish
|
||||||
|
end
|
||||||
|
|
||||||
|
def callback_phase
|
||||||
|
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'])
|
||||||
|
|
||||||
|
request[:auth] = {
|
||||||
|
: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)
|
||||||
|
rescue ::OAuth::Unauthorized
|
||||||
|
fail!(:invalid_credentials)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unique_id
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def full_host
|
||||||
|
uri = URI.parse(request.url)
|
||||||
|
uri.path = ''
|
||||||
|
uri.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
36
lib/omni_auth/strategies/twitter.rb
Normal file
36
lib/omni_auth/strategies/twitter.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
module OmniAuth
|
||||||
|
module Strategies
|
||||||
|
# A convenience wrapper on AuthElsewhere::OAuth to allow you
|
||||||
|
# to declare Twitter more simply.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
#
|
||||||
|
# use AuthElsewhere::Twitter, 'consumerkey', 'consumersecret'
|
||||||
|
#
|
||||||
|
class Twitter < OmniAuth::Strategies::OAuth
|
||||||
|
def initialize(app, consumer_key, consumer_secret)
|
||||||
|
super(app, :twitter, consumer_key, consumer_secret,
|
||||||
|
:site => 'https://api.twitter.com')
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_hash
|
||||||
|
@user_hash ||= JSON.parse(@access_token.get('/1/account/verify_credentials.json'), :symbolize_keys => true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unique_id
|
||||||
|
@access_token.params[:user_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_info
|
||||||
|
{
|
||||||
|
:name => user_hash[:name],
|
||||||
|
:image => user_hash[:profile_image_url],
|
||||||
|
:screen_name => user_hash[:screen_name],
|
||||||
|
:description => user_hash[:description]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
51
lib/omni_auth/strategy.rb
Normal file
51
lib/omni_auth/strategy.rb
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
module OmniAuth
|
||||||
|
module Strategy
|
||||||
|
def self.included(base)
|
||||||
|
base.class_eval do
|
||||||
|
attr_reader :app, :name, :env
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(app, name, *args)
|
||||||
|
@app = app
|
||||||
|
@name = name.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
dup._call(env)
|
||||||
|
end
|
||||||
|
|
||||||
|
def _call(env)
|
||||||
|
@env = env
|
||||||
|
if request.path == "#{OmniAuth.config.path_prefix}/#{name}"
|
||||||
|
request_phase
|
||||||
|
elsif request.path == "#{OmniAuth.config.path_prefix}/#{name}/callback"
|
||||||
|
callback_phase
|
||||||
|
else
|
||||||
|
@app.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def request_phase
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def callback_phase
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def session
|
||||||
|
@env['rack.session']
|
||||||
|
end
|
||||||
|
|
||||||
|
def request
|
||||||
|
@request ||= Rack::Request.new(@env)
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_info; {} end
|
||||||
|
|
||||||
|
def fail!(message_key)
|
||||||
|
OmniAuth.config.on_failure.call(self.env, message_key.to_sym)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
71
spec/auth_elsewhere/oauth_spec.rb
Normal file
71
spec/auth_elsewhere/oauth_spec.rb
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||||
|
require 'auth_elsewhere'
|
||||||
|
|
||||||
|
class Rack::Session::Phony
|
||||||
|
def initialize(app); @app = app end
|
||||||
|
def call(env)
|
||||||
|
env['rack.session'] ||= {}
|
||||||
|
@app.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def app
|
||||||
|
Rack::Builder.new {
|
||||||
|
use Rack::Session::Phony
|
||||||
|
use AuthElsewhere::OAuth, :twitter, 'abc', 'def', :site => 'https://api.twitter.com'
|
||||||
|
use AuthElsewhere::OAuth, :linked_in, 'abc', 'def', :site => 'https://api.linkedin.com'
|
||||||
|
run lambda { |env| [200, {'Content-Type' => 'text/plain'}, env.key?(:oauth).to_s] }
|
||||||
|
}.to_app
|
||||||
|
end
|
||||||
|
|
||||||
|
def session
|
||||||
|
last_request.env['rack.session']
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "AuthElsewhere::OAuth" do
|
||||||
|
before do
|
||||||
|
stub_request(:post, 'https://api.twitter.com/oauth/request_token').
|
||||||
|
to_return(:body => "oauth_token=yourtoken&oauth_token_secret=yoursecret&oauth_callback_confirmed=true")
|
||||||
|
stub_request(:post, 'https://api.twitter.com/oauth/access_token').
|
||||||
|
to_return(:body => "oauth_token=yourtoken&oauth_token_secret=yoursecret")
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '/auth/{name}' do
|
||||||
|
before do
|
||||||
|
get '/auth/twitter'
|
||||||
|
end
|
||||||
|
it 'should redirect to authorize_url' do
|
||||||
|
last_response.should be_redirect
|
||||||
|
last_response.headers['Location'].should == 'https://api.twitter.com/oauth/authorize?oauth_token=yourtoken'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should set appropriate session variables' do
|
||||||
|
session[:oauth].should == {:twitter => {:callback_confirmed => true, :request_token => 'yourtoken', :request_secret => 'yoursecret'}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '/auth/{name}/callback' do
|
||||||
|
before do
|
||||||
|
get '/auth/twitter/callback', {:oauth_verifier => 'dudeman'}, {'rack.session' => {:oauth => {:twitter => {:callback_confirmed => true, :request_token => 'yourtoken', :request_secret => 'yoursecret'}}}}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should exchange the request token for an access token' do
|
||||||
|
last_request.env[:oauth][:provider].should == :twitter
|
||||||
|
last_request.env[:oauth][:access_token].should be_kind_of(OAuth::AccessToken)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should call through to the master app' do
|
||||||
|
last_response.body.should == 'true'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'AuthElsewhere::Twitter' do
|
||||||
|
it 'should subclass OAuth' do
|
||||||
|
AuthElsewhere::Twitter.should < AuthElsewhere::OAuth
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should initialize with just consumer key and secret' do
|
||||||
|
AuthElsewhere::Twitter.new({},'abc','def')
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +0,0 @@
|
||||||
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
||||||
|
|
||||||
describe "RackOauthable" do
|
|
||||||
it "fails" do
|
|
||||||
fail "hey buddy, you should probably rename this file and start specing for real"
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1 +1,3 @@
|
||||||
--color
|
--color
|
||||||
|
--format=specdoc
|
||||||
|
--backtrace
|
|
@ -1,8 +1,15 @@
|
||||||
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
$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 'rack-oauthable'
|
|
||||||
|
require 'rubygems'
|
||||||
|
require 'auth_elsewhere'
|
||||||
require 'spec'
|
require 'spec'
|
||||||
require 'spec/autorun'
|
require 'spec/autorun'
|
||||||
|
require 'rack/test'
|
||||||
|
require 'webmock/rspec'
|
||||||
|
|
||||||
|
include Rack::Test::Methods
|
||||||
|
include WebMock
|
||||||
|
|
||||||
Spec::Runner.configure do |config|
|
Spec::Runner.configure do |config|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue