nov-oauth2 0.1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +33 -0
- data/.rspec +3 -0
- data/CHANGELOG.rdoc +22 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +42 -0
- data/LICENSE +20 -0
- data/README.rdoc +80 -0
- data/Rakefile +31 -0
- data/lib/oauth2.rb +11 -0
- data/lib/oauth2/access_token.rb +47 -0
- data/lib/oauth2/client.rb +88 -0
- data/lib/oauth2/response_object.rb +58 -0
- data/lib/oauth2/strategy/base.rb +29 -0
- data/lib/oauth2/strategy/web_server.rb +44 -0
- data/lib/oauth2/version.rb +3 -0
- data/oauth2.gemspec +25 -0
- data/spec/oauth2/access_token_spec.rb +60 -0
- data/spec/oauth2/client_spec.rb +98 -0
- data/spec/oauth2/strategy/base_spec.rb +7 -0
- data/spec/oauth2/strategy/web_server_spec.rb +69 -0
- data/spec/spec_helper.rb +8 -0
- data/specs.watchr +61 -0
- metadata +191 -0
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
.rvmrc
|
2
|
+
/live
|
3
|
+
|
4
|
+
## MAC OS
|
5
|
+
.DS_Store
|
6
|
+
|
7
|
+
## TEXTMATE
|
8
|
+
*.tmproj
|
9
|
+
tmtags
|
10
|
+
|
11
|
+
## EMACS
|
12
|
+
*~
|
13
|
+
\#*
|
14
|
+
.\#*
|
15
|
+
|
16
|
+
## VIM
|
17
|
+
*.swp
|
18
|
+
|
19
|
+
## PROJECT::GENERAL
|
20
|
+
coverage
|
21
|
+
doc
|
22
|
+
rdoc
|
23
|
+
log
|
24
|
+
|
25
|
+
## BUNDLER
|
26
|
+
*.gem
|
27
|
+
.bundle
|
28
|
+
pkg
|
29
|
+
|
30
|
+
## RCOV
|
31
|
+
coverage.data
|
32
|
+
|
33
|
+
## PROJECT::SPECIFIC
|
data/.rspec
ADDED
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
== 0.0.10 (June 19)
|
2
|
+
|
3
|
+
* Handle ActiveSupport JSON case where incompatible string does not raise an error. (via Flameeyes)
|
4
|
+
* Depend on latest version of MultiJSON.
|
5
|
+
|
6
|
+
== 0.0.9 (June 18)
|
7
|
+
|
8
|
+
* Support a JSON token response with swappable JSON parser via MultiJSON.
|
9
|
+
* Add support for "expires_in" parameter and relevant methods on AccessToken.
|
10
|
+
|
11
|
+
== 0.0.8 (April 27)
|
12
|
+
|
13
|
+
* Change get_request_token to use POST to conform to OAuth 2.0 spec. (via jeremy)
|
14
|
+
|
15
|
+
== 0.0.7 (April 27)
|
16
|
+
|
17
|
+
* Updating Faraday dependency for improved SSL support (via technoweenie)
|
18
|
+
|
19
|
+
== 0.0.6 (April 25)
|
20
|
+
|
21
|
+
* Added ResponseString so as not to throw away response information when making requests.
|
22
|
+
* Deprecated #access_token on WebServer strategy in favor of #get_access_token
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
oauth2 (0.1.1)
|
5
|
+
faraday (~> 0.5.0)
|
6
|
+
multi_json (~> 0.0.4)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
addressable (2.1.2)
|
12
|
+
diff-lcs (1.1.2)
|
13
|
+
faraday (0.5.0)
|
14
|
+
addressable (~> 2.1.1)
|
15
|
+
multipart-post (~> 1.0.1)
|
16
|
+
rack (~> 1.2.1)
|
17
|
+
json_pure (1.4.6)
|
18
|
+
multi_json (0.0.4)
|
19
|
+
multipart-post (1.0.1)
|
20
|
+
rack (1.2.1)
|
21
|
+
rake (0.8.7)
|
22
|
+
rcov (0.9.9)
|
23
|
+
rspec (2.4.0)
|
24
|
+
rspec-core (~> 2.4.0)
|
25
|
+
rspec-expectations (~> 2.4.0)
|
26
|
+
rspec-mocks (~> 2.4.0)
|
27
|
+
rspec-core (2.4.0)
|
28
|
+
rspec-expectations (2.4.0)
|
29
|
+
diff-lcs (~> 1.1.2)
|
30
|
+
rspec-mocks (2.4.0)
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
ruby
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
faraday (~> 0.5.0)
|
37
|
+
json_pure (~> 1.4.6)
|
38
|
+
multi_json (~> 0.0.4)
|
39
|
+
oauth2!
|
40
|
+
rake (~> 0.8)
|
41
|
+
rcov (~> 0.9)
|
42
|
+
rspec (~> 2.4.0)
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Intridea, Inc. and Michael Bleigh
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
= OAuth2
|
2
|
+
|
3
|
+
A Ruby wrapper for the OAuth 2.0 specification. This is a work in progress, being built first to solve the pragmatic process of connecting to existing OAuth 2.0 endpoints (a.k.a. Facebook) with the goal of building it up to meet the entire specification over time.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
gem install oauth2
|
8
|
+
|
9
|
+
== Resources
|
10
|
+
|
11
|
+
* View Source on GitHub (http://github.com/intridea/oauth2)
|
12
|
+
* Report Issues on GitHub (http://github.com/intridea/oauth2/issues)
|
13
|
+
* Read More at the Wiki (http://wiki.github.com/intridea/oauth2/)
|
14
|
+
|
15
|
+
== Web Server Example (Sinatra)
|
16
|
+
|
17
|
+
Below is a fully functional example of a Sinatra application that would authenticate to Facebook utilizing the OAuth 2.0 web server flow.
|
18
|
+
|
19
|
+
require 'rubygems'
|
20
|
+
require 'sinatra'
|
21
|
+
require 'oauth2'
|
22
|
+
require 'json'
|
23
|
+
|
24
|
+
def client
|
25
|
+
OAuth2::Client.new('app_id', 'app_secret', :site => 'https://graph.facebook.com')
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/auth/facebook' do
|
29
|
+
redirect client.web_server.authorize_url(
|
30
|
+
:redirect_uri => redirect_uri,
|
31
|
+
:scope => 'email,offline_access'
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
get '/auth/facebook/callback' do
|
36
|
+
access_token = client.web_server.get_access_token(params[:code], :redirect_uri => redirect_uri)
|
37
|
+
user = JSON.parse(access_token.get('/me'))
|
38
|
+
|
39
|
+
user.inspect
|
40
|
+
end
|
41
|
+
|
42
|
+
def redirect_uri
|
43
|
+
uri = URI.parse(request.url)
|
44
|
+
uri.path = '/auth/facebook/callback'
|
45
|
+
uri.query = nil
|
46
|
+
uri.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
That's all there is to it! You can use the access token like you would with the
|
50
|
+
OAuth gem, calling HTTP verbs on it etc. You can view more examples on the OAuth2
|
51
|
+
Wiki (http://wiki.github.com/intridea/oauth2/examples)
|
52
|
+
|
53
|
+
== JSON Parsing
|
54
|
+
|
55
|
+
Because JSON has become the standard format of the OAuth 2.0 specification,
|
56
|
+
the <tt>oauth2</tt> gem contains a mode that will perform automatic parsing
|
57
|
+
of JSON response bodies, returning a hash instead of a string. To enable this
|
58
|
+
mode, simply add the <tt>:parse_json</tt> option to your client initialization:
|
59
|
+
|
60
|
+
client = OAuth2::Client.new('app_id', 'app_secret',
|
61
|
+
:site => 'https://example.com',
|
62
|
+
:parse_json => true
|
63
|
+
)
|
64
|
+
|
65
|
+
# Obtain an access token using the client
|
66
|
+
token.get('/some/url.json') # {"some" => "hash"}
|
67
|
+
|
68
|
+
== Note on Patches/Pull Requests
|
69
|
+
|
70
|
+
* Fork the project.
|
71
|
+
* Make your feature addition or bug fix.
|
72
|
+
* Add tests for it. This is important so I don't break it in a
|
73
|
+
future version unintentionally.
|
74
|
+
* Commit, do not mess with rakefile, version, or history.
|
75
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
76
|
+
* Send me a pull request. Bonus points for topic branches.
|
77
|
+
|
78
|
+
== Copyright
|
79
|
+
|
80
|
+
Copyright (c) 2010 Intridea, Inc. and Michael Bleigh. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
desc "Run all examples"
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
7
|
+
t.rspec_opts = %w[--color]
|
8
|
+
end
|
9
|
+
|
10
|
+
task :cleanup_rcov_files do
|
11
|
+
rm_rf 'coverage.data'
|
12
|
+
end
|
13
|
+
|
14
|
+
namespace :spec do
|
15
|
+
desc "Run all examples using rcov"
|
16
|
+
RSpec::Core::RakeTask.new :rcov => :cleanup_rcov_files do |t|
|
17
|
+
t.rcov = true
|
18
|
+
t.rcov_opts = %[-Ilib -Ispec --exclude "gems/*,features"]
|
19
|
+
t.rcov_opts << %[--text-report --sort coverage --no-html --aggregate coverage.data]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
task :default => :spec
|
24
|
+
|
25
|
+
require 'rake/rdoctask'
|
26
|
+
Rake::RDocTask.new do |rdoc|
|
27
|
+
rdoc.rdoc_dir = 'rdoc'
|
28
|
+
rdoc.title = "oauth2 #{OAuth2::VERSION}"
|
29
|
+
rdoc.rdoc_files.include('README*')
|
30
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
31
|
+
end
|
data/lib/oauth2.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
module OAuth2
|
2
|
+
class ErrorWithResponse < StandardError; attr_accessor :response end
|
3
|
+
class AccessDenied < ErrorWithResponse; end
|
4
|
+
class HTTPError < ErrorWithResponse; end
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'oauth2/client'
|
8
|
+
require 'oauth2/strategy/base'
|
9
|
+
require 'oauth2/strategy/web_server'
|
10
|
+
require 'oauth2/access_token'
|
11
|
+
require 'oauth2/response_object'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module OAuth2
|
2
|
+
class AccessToken
|
3
|
+
attr_reader :client, :token, :refresh_token, :expires_in, :expires_at
|
4
|
+
attr_accessor :token_param
|
5
|
+
|
6
|
+
def initialize(client, token, refresh_token = nil, expires_in = nil, params = {})
|
7
|
+
@client = client
|
8
|
+
@token = token.to_s
|
9
|
+
@refresh_token = refresh_token.to_s
|
10
|
+
@expires_in = (expires_in.nil? || expires_in == '') ? nil : expires_in.to_i
|
11
|
+
@expires_at = Time.now + @expires_in if @expires_in
|
12
|
+
@params = params
|
13
|
+
@token_param = 'oauth_token'
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](key)
|
17
|
+
@params[key]
|
18
|
+
end
|
19
|
+
|
20
|
+
# True if the token in question has an expiration time.
|
21
|
+
def expires?
|
22
|
+
!!@expires_in
|
23
|
+
end
|
24
|
+
|
25
|
+
def request(verb, path, params = {}, headers = {})
|
26
|
+
params = params.merge token_param => @token
|
27
|
+
headers = headers.merge 'Authorization' => "OAuth #{@token}"
|
28
|
+
@client.request(verb, path, params, headers)
|
29
|
+
end
|
30
|
+
|
31
|
+
def get(path, params = {}, headers = {})
|
32
|
+
request(:get, path, params, headers)
|
33
|
+
end
|
34
|
+
|
35
|
+
def post(path, params = {}, headers = {})
|
36
|
+
request(:post, path, params, headers)
|
37
|
+
end
|
38
|
+
|
39
|
+
def put(path, params = {}, headers = {})
|
40
|
+
request(:put, path, params, headers)
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete(path, params = {}, headers = {})
|
44
|
+
request(:delete, path, params, headers)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module OAuth2
|
4
|
+
class Client
|
5
|
+
class << self
|
6
|
+
def default_connection_adapter
|
7
|
+
warn '[DEPRECATED] OAuth2::Client#default_connection_adapter is deprecated, use Faraday.default_adapter instead. Will be removed in 0.1.0'
|
8
|
+
Faraday.default_adapter
|
9
|
+
end
|
10
|
+
|
11
|
+
def default_connection_adapter=(adapter)
|
12
|
+
warn '[DEPRECATED] OAuth2::Client#default_connection_adapter is deprecated, use Faraday.default_adapter instead. Will be removed in 0.1.0'
|
13
|
+
Faraday.default_adapter = adapter
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_accessor :id, :secret, :site, :connection, :options
|
18
|
+
attr_writer :json
|
19
|
+
|
20
|
+
# Instantiate a new OAuth 2.0 client using the
|
21
|
+
# client ID and client secret registered to your
|
22
|
+
# application.
|
23
|
+
#
|
24
|
+
# Options:
|
25
|
+
#
|
26
|
+
# <tt>:site</tt> :: Specify a base URL for your OAuth 2.0 client.
|
27
|
+
# <tt>:authorize_path</tt> :: Specify the path to the authorization endpoint.
|
28
|
+
# <tt>:authorize_url</tt> :: Specify a full URL of the authorization endpoint.
|
29
|
+
# <tt>:access_token_path</tt> :: Specify the path to the access token endpoint.
|
30
|
+
# <tt>:access_token_url</tt> :: Specify the full URL of the access token endpoint.
|
31
|
+
# <tt>:parse_json</tt> :: If true, <tt>application/json</tt> responses will be automatically parsed.
|
32
|
+
def initialize(client_id, client_secret, opts = {})
|
33
|
+
adapter = opts.delete(:adapter)
|
34
|
+
self.id = client_id
|
35
|
+
self.secret = client_secret
|
36
|
+
self.site = opts.delete(:site) if opts[:site]
|
37
|
+
self.options = opts
|
38
|
+
self.connection = Faraday::Connection.new(site)
|
39
|
+
self.json = opts.delete(:parse_json)
|
40
|
+
|
41
|
+
if adapter && adapter != :test
|
42
|
+
connection.build { |b| b.adapter(adapter) }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def authorize_url(params = nil)
|
47
|
+
path = options[:authorize_url] || options[:authorize_path] || "/oauth/authorize"
|
48
|
+
connection.build_url(path, params).to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
def access_token_url(params = nil)
|
52
|
+
path = options[:access_token_url] || options[:access_token_path] || "/oauth/access_token"
|
53
|
+
connection.build_url(path, params).to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
# Makes a request relative to the specified site root.
|
57
|
+
def request(verb, url, params = {}, headers = {})
|
58
|
+
if verb == :get
|
59
|
+
resp = connection.run_request(verb, url, nil, headers) do |req|
|
60
|
+
req.params.update(params)
|
61
|
+
end
|
62
|
+
else
|
63
|
+
resp = connection.run_request(verb, url, params, headers)
|
64
|
+
end
|
65
|
+
|
66
|
+
case resp.status
|
67
|
+
when 200..299
|
68
|
+
if json?
|
69
|
+
return ResponseObject.from(resp)
|
70
|
+
else
|
71
|
+
return ResponseString.new(resp)
|
72
|
+
end
|
73
|
+
when 401
|
74
|
+
e = OAuth2::AccessDenied.new("Received HTTP 401 during request.")
|
75
|
+
e.response = resp
|
76
|
+
raise e
|
77
|
+
else
|
78
|
+
e = OAuth2::HTTPError.new("Received HTTP #{resp.status} during request.")
|
79
|
+
e.response = resp
|
80
|
+
raise e
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def json?; !!@json end
|
85
|
+
|
86
|
+
def web_server; OAuth2::Strategy::WebServer.new(self) end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module OAuth2
|
2
|
+
module ResponseObject
|
3
|
+
def self.from(response)
|
4
|
+
object = MultiJson.decode(response.body)
|
5
|
+
|
6
|
+
case object
|
7
|
+
when Array
|
8
|
+
ResponseArray.new(response, object)
|
9
|
+
when Hash
|
10
|
+
ResponseHash.new(response, object)
|
11
|
+
else
|
12
|
+
ResponseString.new(response)
|
13
|
+
end
|
14
|
+
rescue
|
15
|
+
ResponseString.new(response)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.included(base)
|
19
|
+
base.class_eval do
|
20
|
+
attr_accessor :response
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def headers; response.headers end
|
25
|
+
def status; response.status end
|
26
|
+
end
|
27
|
+
|
28
|
+
class ResponseHash < Hash
|
29
|
+
include ResponseObject
|
30
|
+
|
31
|
+
def initialize(response, hash)
|
32
|
+
self.response = response
|
33
|
+
hash.keys.each{|k| self[k] = hash[k]}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class ResponseArray < Array
|
38
|
+
include ResponseObject
|
39
|
+
|
40
|
+
def initialize(response, array)
|
41
|
+
self.response = response
|
42
|
+
super(array)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# This special String class is returned from HTTP requests
|
47
|
+
# and contains the original full response along with convenience
|
48
|
+
# methods for accessing the HTTP status code and headers. It
|
49
|
+
# is returned from all access token requests.
|
50
|
+
class ResponseString < String
|
51
|
+
include ResponseObject
|
52
|
+
|
53
|
+
def initialize(response)
|
54
|
+
super(response.body)
|
55
|
+
self.response = response
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module OAuth2
|
2
|
+
module Strategy
|
3
|
+
class Base #:nodoc:
|
4
|
+
def initialize(client)#:nodoc:
|
5
|
+
@client = client
|
6
|
+
end
|
7
|
+
|
8
|
+
def authorize_url(options = {}) #:nodoc:
|
9
|
+
@client.authorize_url(authorize_params(options))
|
10
|
+
end
|
11
|
+
|
12
|
+
def authorize_params(options = {}) #:nodoc:
|
13
|
+
options = options.inject({}){|h, (k, v)| h[k.to_s] = v; h}
|
14
|
+
{'client_id' => @client.id}.merge(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def access_token_url(options = {})
|
18
|
+
@client.access_token_url(access_token_params(options))
|
19
|
+
end
|
20
|
+
|
21
|
+
def access_token_params(options = {})
|
22
|
+
{
|
23
|
+
'client_id' => @client.id,
|
24
|
+
'client_secret' => @client.secret
|
25
|
+
}.merge(options)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
module OAuth2
|
4
|
+
module Strategy
|
5
|
+
class WebServer < Base
|
6
|
+
def authorize_params(options = {}) #:nodoc:
|
7
|
+
super(options).merge('response_type' => 'code')
|
8
|
+
end
|
9
|
+
|
10
|
+
# Retrieve an access token given the specified validation code.
|
11
|
+
# Note that you must also provide a <tt>:redirect_uri</tt> option
|
12
|
+
# in order to successfully verify your request for most OAuth 2.0
|
13
|
+
# endpoints.
|
14
|
+
def get_access_token(code, options = {})
|
15
|
+
response = @client.request(:post, @client.access_token_url, access_token_params(code, options))
|
16
|
+
|
17
|
+
params = MultiJson.decode(response) rescue nil
|
18
|
+
# the ActiveSupport JSON parser won't cause an exception when
|
19
|
+
# given a formencoded string, so make sure that it was
|
20
|
+
# actually parsed in an Hash. This covers even the case where
|
21
|
+
# it caused an exception since it'll still be nil.
|
22
|
+
params = Rack::Utils.parse_query(response) unless params.is_a? Hash
|
23
|
+
|
24
|
+
access = params['access_token']
|
25
|
+
refresh = params['refresh_token']
|
26
|
+
expires_in = params['expires_in'] || params['expires'] # params['expires'] is only for facebook
|
27
|
+
OAuth2::AccessToken.new(@client, access, refresh, expires_in, params)
|
28
|
+
end
|
29
|
+
|
30
|
+
# <b>DEPRECATED:</b> Use #get_access_token instead.
|
31
|
+
def access_token(*args)
|
32
|
+
warn '[DEPRECATED] OAuth2::Strategy::WebServer#access_token is deprecated, use #get_access_token instead. Will be removed in 0.1.0'
|
33
|
+
get_access_token(*args)
|
34
|
+
end
|
35
|
+
|
36
|
+
def access_token_params(code, options = {}) #:nodoc:
|
37
|
+
super(options).merge({
|
38
|
+
'grant_type' => 'authorization_code',
|
39
|
+
'code' => code
|
40
|
+
})
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/oauth2.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path("../lib/oauth2/version", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "nov-oauth2"
|
6
|
+
s.version = OAuth2::VERSION
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.3.6") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Nov Matake", "Michael Bleigh"]
|
9
|
+
s.description = %q{A Ruby wrapper for the OAuth 2.0 protocol built with a similar style to the original OAuth gem.}
|
10
|
+
s.summary = %q{Michael Bleigh's OAuth Gem with OAuth 2.0 Draft v.10 support.}
|
11
|
+
s.email = "nov@matake.com"
|
12
|
+
s.extra_rdoc_files = ["LICENSE", "README.rdoc"]
|
13
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
14
|
+
s.homepage = "http://github.com/nov/oauth2"
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.add_runtime_dependency("faraday", "~> 0.5.0")
|
20
|
+
s.add_runtime_dependency("multi_json", "~> 0.0.4")
|
21
|
+
s.add_development_dependency("json_pure", "~> 1.4.6")
|
22
|
+
s.add_development_dependency("rake", "~> 0.8")
|
23
|
+
s.add_development_dependency("rcov", "~> 0.9")
|
24
|
+
s.add_development_dependency("rspec", "~> 2.4.0")
|
25
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OAuth2::AccessToken do
|
4
|
+
let(:client) do
|
5
|
+
cli = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com')
|
6
|
+
cli.connection.build do |b|
|
7
|
+
b.adapter :test do |stub|
|
8
|
+
stub.get('/client?oauth_token=monkey') { |env| [200, {}, 'get'] }
|
9
|
+
stub.post('/client') { |env| [200, {}, 'oauth_token=' << env[:body]['oauth_token']] }
|
10
|
+
stub.put('/client') { |env| [200, {}, 'oauth_token=' << env[:body]['oauth_token']] }
|
11
|
+
stub.delete('/client') { |env| [200, {}, 'oauth_token=' << env[:body]['oauth_token']] }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
cli
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:token) { 'monkey' }
|
18
|
+
|
19
|
+
subject { OAuth2::AccessToken.new(client, token) }
|
20
|
+
|
21
|
+
describe '#initialize' do
|
22
|
+
it 'should assign client and token' do
|
23
|
+
subject.client.should == client
|
24
|
+
subject.token.should == token
|
25
|
+
end
|
26
|
+
|
27
|
+
it "makes GET requests with access token" do
|
28
|
+
subject.send(:get, 'client').should == 'get'
|
29
|
+
end
|
30
|
+
|
31
|
+
%w(post put delete).each do |http_method|
|
32
|
+
it "makes #{http_method.upcase} requests with access token" do
|
33
|
+
subject.send(http_method.to_sym, 'client').should == 'oauth_token=monkey'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#expires?' do
|
39
|
+
it 'should be false if there is no expires_at' do
|
40
|
+
OAuth2::AccessToken.new(client, token).should_not be_expires
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should be true if there is an expires_at' do
|
44
|
+
OAuth2::AccessToken.new(client, token, 'abaca', 600).should be_expires
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#expires_at' do
|
49
|
+
before do
|
50
|
+
@now = Time.now
|
51
|
+
Time.stub!(:now).and_return(@now)
|
52
|
+
end
|
53
|
+
|
54
|
+
subject{ OAuth2::AccessToken.new(client, token, 'abaca', 600)}
|
55
|
+
|
56
|
+
it 'should be a time representation of #expires_in' do
|
57
|
+
subject.expires_at.should == (@now + 600)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OAuth2::Client do
|
4
|
+
subject do
|
5
|
+
cli = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com')
|
6
|
+
cli.connection.build do |b|
|
7
|
+
b.adapter :test do |stub|
|
8
|
+
stub.get('/success') { |env| [200, {'Content-Type' => 'text/awesome'}, 'yay'] }
|
9
|
+
stub.get('/unauthorized') { |env| [401, {}, ''] }
|
10
|
+
stub.get('/error') { |env| [500, {}, ''] }
|
11
|
+
stub.get('/json') { |env| [200, {'Content-Type' => 'application/json; charset=utf8'}, '{"abc":"def"}']}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
cli
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#initialize' do
|
18
|
+
it 'should assign id and secret' do
|
19
|
+
subject.id.should == 'abc'
|
20
|
+
subject.secret.should == 'def'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should assign site from the options hash' do
|
24
|
+
subject.site.should == 'https://api.example.com'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should assign Faraday::Connection#host' do
|
28
|
+
subject.connection.host.should == 'api.example.com'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
%w(authorize access_token).each do |path_type|
|
33
|
+
describe "##{path_type}_url" do
|
34
|
+
it "should default to a path of /oauth/#{path_type}" do
|
35
|
+
subject.send("#{path_type}_url").should == "https://api.example.com/oauth/#{path_type}"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should be settable via the :#{path_type}_path option" do
|
39
|
+
subject.options[:"#{path_type}_path"] = '/oauth/custom'
|
40
|
+
subject.send("#{path_type}_url").should == 'https://api.example.com/oauth/custom'
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should be settable via the :#{path_type}_url option" do
|
44
|
+
subject.options[:"#{path_type}_url"] = 'https://abc.com/authorize'
|
45
|
+
subject.send("#{path_type}_url").should == 'https://abc.com/authorize'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#request" do
|
51
|
+
it "returns ResponseString on successful response" do
|
52
|
+
response = subject.request(:get, '/success', {}, {})
|
53
|
+
response.should == 'yay'
|
54
|
+
response.status.should == 200
|
55
|
+
response.headers.should == {'Content-Type' => 'text/awesome'}
|
56
|
+
end
|
57
|
+
|
58
|
+
it "raises OAuth2::AccessDenied on 401 response" do
|
59
|
+
lambda { subject.request(:get, '/unauthorized', {}, {}) }.should raise_error(OAuth2::AccessDenied)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "raises OAuth2::HTTPError on error response" do
|
63
|
+
lambda { subject.request(:get, '/error', {}, {}) }.should raise_error(OAuth2::HTTPError)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it '#web_server should instantiate a WebServer strategy with this client' do
|
68
|
+
subject.web_server.should be_kind_of(OAuth2::Strategy::WebServer)
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with JSON parsing' do
|
72
|
+
before do
|
73
|
+
subject.json = true
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#request' do
|
77
|
+
it 'should return a response hash' do
|
78
|
+
response = subject.request(:get, '/json')
|
79
|
+
puts response.inspect
|
80
|
+
response.should be_kind_of(OAuth2::ResponseHash)
|
81
|
+
response['abc'].should == 'def'
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should only try to decode application/json' do
|
85
|
+
subject.request(:get, '/success').should == 'yay'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should set json? based on the :parse_json option' do
|
90
|
+
OAuth2::Client.new('abc', 'def', :site => 'http://example.com', :parse_json => true).should be_json
|
91
|
+
OAuth2::Client.new('abc', 'def', :site => 'http://example.com', :parse_json => false).should_not be_json
|
92
|
+
end
|
93
|
+
|
94
|
+
after do
|
95
|
+
subject.json = false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OAuth2::Strategy::WebServer do
|
4
|
+
let(:client) do
|
5
|
+
cli = OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com')
|
6
|
+
cli.connection.build do |b|
|
7
|
+
b.adapter :test do |stub|
|
8
|
+
stub.post('/oauth/access_token') do |env|
|
9
|
+
case @mode
|
10
|
+
when "formencoded"
|
11
|
+
[200, {}, 'expires_in=600&access_token=salmon&refresh_token=trout&extra_param=steve']
|
12
|
+
when "json"
|
13
|
+
[200, {}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout","extra_param":"steve"}']
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
cli
|
19
|
+
end
|
20
|
+
subject { client.web_server }
|
21
|
+
|
22
|
+
describe '#authorize_url' do
|
23
|
+
it 'should include the client_id' do
|
24
|
+
subject.authorize_url.should be_include('client_id=abc')
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should include the type' do
|
28
|
+
subject.authorize_url.should be_include('response_type=code')
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should include passed in options' do
|
32
|
+
cb = 'http://myserver.local/oauth/callback'
|
33
|
+
subject.authorize_url(:redirect_uri => cb).should be_include("redirect_uri=#{Rack::Utils.escape(cb)}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
%w(json formencoded).each do |mode|
|
38
|
+
describe "#get_access_token (#{mode})" do
|
39
|
+
before do
|
40
|
+
@mode = mode
|
41
|
+
@access = subject.get_access_token('sushi')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns AccessToken with same Client' do
|
45
|
+
@access.client.should == client
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'returns AccessToken with #token' do
|
49
|
+
@access.token.should == 'salmon'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns AccessToken with #refresh_token' do
|
53
|
+
@access.refresh_token.should == 'trout'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'returns AccessToken with #expires_in' do
|
57
|
+
@access.expires_in.should == 600
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'returns AccessToken with #expires_at' do
|
61
|
+
@access.expires_at.should be_kind_of(Time)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'returns AccessToken with params accessible via []' do
|
65
|
+
@access['extra_param'].should == 'steve'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/specs.watchr
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# Run me with:
|
2
|
+
#
|
3
|
+
# $ watchr specs.watchr
|
4
|
+
|
5
|
+
# --------------------------------------------------
|
6
|
+
# Convenience Methods
|
7
|
+
# --------------------------------------------------
|
8
|
+
def all_test_files
|
9
|
+
Dir['spec/**/*_spec.rb']
|
10
|
+
end
|
11
|
+
|
12
|
+
def run_test_matching(thing_to_match)
|
13
|
+
matches = all_test_files.grep(/#{thing_to_match}/i)
|
14
|
+
if matches.empty?
|
15
|
+
puts "Sorry, thanks for playing, but there were no matches for #{thing_to_match}"
|
16
|
+
else
|
17
|
+
run matches.join(' ')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def run(files_to_run)
|
22
|
+
puts("Running: #{files_to_run}")
|
23
|
+
system("clear;spec -cfs --backtrace #{files_to_run}")
|
24
|
+
no_int_for_you
|
25
|
+
end
|
26
|
+
|
27
|
+
def run_all_tests
|
28
|
+
# system("clear;rake spec")
|
29
|
+
run(all_test_files.join(' '))
|
30
|
+
end
|
31
|
+
|
32
|
+
# --------------------------------------------------
|
33
|
+
# Watchr Rules
|
34
|
+
# --------------------------------------------------
|
35
|
+
watch('^spec/(.*)_spec\.rb') { |m| run_test_matching(m[1]) }
|
36
|
+
watch('^lib/(.*)\.rb') { |m| run_test_matching(m[1]) }
|
37
|
+
watch('^sites/(.*)\.rb') { |m| run_test_matching(m[1]) }
|
38
|
+
watch('^spec/spec_helper\.rb') { run_all_tests }
|
39
|
+
watch('^spec/support/.*\.rb') { run_all_tests }
|
40
|
+
|
41
|
+
# --------------------------------------------------
|
42
|
+
# Signal Handling
|
43
|
+
# --------------------------------------------------
|
44
|
+
|
45
|
+
def no_int_for_you
|
46
|
+
@sent_an_int = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
Signal.trap 'INT' do
|
50
|
+
if @sent_an_int then
|
51
|
+
puts " A second INT? Ok, I get the message. Shutting down now."
|
52
|
+
exit
|
53
|
+
else
|
54
|
+
puts " Did you just send me an INT? Ugh. I'll quit for real if you do it again."
|
55
|
+
@sent_an_int = true
|
56
|
+
Kernel.sleep 1.5
|
57
|
+
run_all_tests
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# vim:ft=ruby
|
metadata
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nov-oauth2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 65
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
- 1
|
11
|
+
version: 0.1.1.1
|
12
|
+
platform: ruby
|
13
|
+
authors:
|
14
|
+
- Nov Matake
|
15
|
+
- Michael Bleigh
|
16
|
+
autorequire:
|
17
|
+
bindir: bin
|
18
|
+
cert_chain: []
|
19
|
+
|
20
|
+
date: 2011-03-09 00:00:00 +09:00
|
21
|
+
default_executable:
|
22
|
+
dependencies:
|
23
|
+
- !ruby/object:Gem::Dependency
|
24
|
+
name: faraday
|
25
|
+
prerelease: false
|
26
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ~>
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
hash: 11
|
32
|
+
segments:
|
33
|
+
- 0
|
34
|
+
- 5
|
35
|
+
- 0
|
36
|
+
version: 0.5.0
|
37
|
+
type: :runtime
|
38
|
+
version_requirements: *id001
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
name: multi_json
|
41
|
+
prerelease: false
|
42
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
hash: 23
|
48
|
+
segments:
|
49
|
+
- 0
|
50
|
+
- 0
|
51
|
+
- 4
|
52
|
+
version: 0.0.4
|
53
|
+
type: :runtime
|
54
|
+
version_requirements: *id002
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: json_pure
|
57
|
+
prerelease: false
|
58
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ~>
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
hash: 11
|
64
|
+
segments:
|
65
|
+
- 1
|
66
|
+
- 4
|
67
|
+
- 6
|
68
|
+
version: 1.4.6
|
69
|
+
type: :development
|
70
|
+
version_requirements: *id003
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: rake
|
73
|
+
prerelease: false
|
74
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ~>
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
hash: 27
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
- 8
|
83
|
+
version: "0.8"
|
84
|
+
type: :development
|
85
|
+
version_requirements: *id004
|
86
|
+
- !ruby/object:Gem::Dependency
|
87
|
+
name: rcov
|
88
|
+
prerelease: false
|
89
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ~>
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
hash: 25
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
- 9
|
98
|
+
version: "0.9"
|
99
|
+
type: :development
|
100
|
+
version_requirements: *id005
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
name: rspec
|
103
|
+
prerelease: false
|
104
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
hash: 31
|
110
|
+
segments:
|
111
|
+
- 2
|
112
|
+
- 4
|
113
|
+
- 0
|
114
|
+
version: 2.4.0
|
115
|
+
type: :development
|
116
|
+
version_requirements: *id006
|
117
|
+
description: A Ruby wrapper for the OAuth 2.0 protocol built with a similar style to the original OAuth gem.
|
118
|
+
email: nov@matake.com
|
119
|
+
executables: []
|
120
|
+
|
121
|
+
extensions: []
|
122
|
+
|
123
|
+
extra_rdoc_files:
|
124
|
+
- LICENSE
|
125
|
+
- README.rdoc
|
126
|
+
files:
|
127
|
+
- .document
|
128
|
+
- .gitignore
|
129
|
+
- .rspec
|
130
|
+
- CHANGELOG.rdoc
|
131
|
+
- Gemfile
|
132
|
+
- Gemfile.lock
|
133
|
+
- LICENSE
|
134
|
+
- README.rdoc
|
135
|
+
- Rakefile
|
136
|
+
- lib/oauth2.rb
|
137
|
+
- lib/oauth2/access_token.rb
|
138
|
+
- lib/oauth2/client.rb
|
139
|
+
- lib/oauth2/response_object.rb
|
140
|
+
- lib/oauth2/strategy/base.rb
|
141
|
+
- lib/oauth2/strategy/web_server.rb
|
142
|
+
- lib/oauth2/version.rb
|
143
|
+
- oauth2.gemspec
|
144
|
+
- spec/oauth2/access_token_spec.rb
|
145
|
+
- spec/oauth2/client_spec.rb
|
146
|
+
- spec/oauth2/strategy/base_spec.rb
|
147
|
+
- spec/oauth2/strategy/web_server_spec.rb
|
148
|
+
- spec/spec_helper.rb
|
149
|
+
- specs.watchr
|
150
|
+
has_rdoc: true
|
151
|
+
homepage: http://github.com/nov/oauth2
|
152
|
+
licenses: []
|
153
|
+
|
154
|
+
post_install_message:
|
155
|
+
rdoc_options:
|
156
|
+
- --charset=UTF-8
|
157
|
+
require_paths:
|
158
|
+
- lib
|
159
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
|
+
none: false
|
161
|
+
requirements:
|
162
|
+
- - ">="
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
hash: 3
|
165
|
+
segments:
|
166
|
+
- 0
|
167
|
+
version: "0"
|
168
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
hash: 23
|
174
|
+
segments:
|
175
|
+
- 1
|
176
|
+
- 3
|
177
|
+
- 6
|
178
|
+
version: 1.3.6
|
179
|
+
requirements: []
|
180
|
+
|
181
|
+
rubyforge_project:
|
182
|
+
rubygems_version: 1.5.3
|
183
|
+
signing_key:
|
184
|
+
specification_version: 3
|
185
|
+
summary: Michael Bleigh's OAuth Gem with OAuth 2.0 Draft v.10 support.
|
186
|
+
test_files:
|
187
|
+
- spec/oauth2/access_token_spec.rb
|
188
|
+
- spec/oauth2/client_spec.rb
|
189
|
+
- spec/oauth2/strategy/base_spec.rb
|
190
|
+
- spec/oauth2/strategy/web_server_spec.rb
|
191
|
+
- spec/spec_helper.rb
|