oauth2 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.travis.yml +7 -0
- data/{CHANGELOG.rdoc → CHANGELOG.md} +11 -11
- data/{LICENSE → LICENSE.md} +1 -1
- data/README.md +95 -0
- data/lib/oauth2.rb +1 -0
- data/lib/oauth2/access_token.rb +7 -7
- data/lib/oauth2/client.rb +47 -27
- data/lib/oauth2/strategy/base.rb +4 -4
- data/lib/oauth2/strategy/password.rb +38 -0
- data/lib/oauth2/strategy/web_server.rb +12 -17
- data/lib/oauth2/version.rb +1 -1
- data/oauth2.gemspec +0 -2
- data/spec/oauth2/access_token_spec.rb +13 -7
- data/spec/oauth2/client_spec.rb +56 -6
- data/spec/oauth2/strategy/base_spec.rb +1 -1
- data/spec/oauth2/strategy/password_spec.rb +57 -0
- data/spec/oauth2/strategy/web_server_spec.rb +8 -8
- metadata +15 -13
- data/README.rdoc +0 -80
data/.gitignore
CHANGED
data/.travis.yml
ADDED
@@ -1,22 +1,22 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
0.0.10 - June 19, 2010
|
2
|
+
----------------------
|
3
3
|
* Handle ActiveSupport JSON case where incompatible string does not raise an error. (via Flameeyes)
|
4
4
|
* Depend on latest version of MultiJSON.
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
0.0.9 - June 18, 2010
|
7
|
+
---------------------
|
8
8
|
* Support a JSON token response with swappable JSON parser via MultiJSON.
|
9
9
|
* Add support for "expires_in" parameter and relevant methods on AccessToken.
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
0.0.8 - April 27, 2010
|
12
|
+
----------------------
|
13
13
|
* Change get_request_token to use POST to conform to OAuth 2.0 spec. (via jeremy)
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
0.0.7 - April 27, 2010
|
16
|
+
----------------------
|
17
17
|
* Updating Faraday dependency for improved SSL support (via technoweenie)
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
0.0.6 - April 25, 2010
|
20
|
+
----------------------
|
21
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
|
22
|
+
* Deprecated #access_token on WebServer strategy in favor of #get_access_token
|
data/{LICENSE → LICENSE.md}
RENAMED
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
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
|
+
Continuous Integration
|
10
|
+
----------------------
|
11
|
+
[![Build Status](http://travis-ci.org/intridea/oauth2.png)](http://travis-ci.org/intridea/oauth2)
|
12
|
+
|
13
|
+
Resources
|
14
|
+
---------
|
15
|
+
* View Source on GitHub (https://github.com/intridea/oauth2)
|
16
|
+
* Report Issues on GitHub (https://github.com/intridea/oauth2/issues)
|
17
|
+
* Read More at the Wiki (https://wiki.github.com/intridea/oauth2)
|
18
|
+
|
19
|
+
Web Server Example (Sinatra)
|
20
|
+
----------------------------
|
21
|
+
Below is a fully functional example of a Sinatra application that would authenticate to Facebook utilizing the OAuth 2.0 web server flow.
|
22
|
+
|
23
|
+
require 'rubygems'
|
24
|
+
require 'sinatra'
|
25
|
+
require 'oauth2'
|
26
|
+
require 'json'
|
27
|
+
|
28
|
+
def client
|
29
|
+
OAuth2::Client.new('app_id', 'app_secret', :site => 'https://graph.facebook.com')
|
30
|
+
end
|
31
|
+
|
32
|
+
get '/auth/facebook' do
|
33
|
+
redirect client.web_server.authorize_url(
|
34
|
+
:redirect_uri => redirect_uri,
|
35
|
+
:scope => 'email,offline_access'
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
get '/auth/facebook/callback' do
|
40
|
+
access_token = client.web_server.get_access_token(params[:code], :redirect_uri => redirect_uri)
|
41
|
+
user = JSON.parse(access_token.get('/me'))
|
42
|
+
user.inspect
|
43
|
+
end
|
44
|
+
|
45
|
+
def redirect_uri
|
46
|
+
uri = URI.parse(request.url)
|
47
|
+
uri.path = '/auth/facebook/callback'
|
48
|
+
uri.query = nil
|
49
|
+
uri.to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
That's all there is to it! You can use the access token like you would with the
|
53
|
+
OAuth gem, calling HTTP verbs on it etc. You can view more examples on the [OAuth2
|
54
|
+
Wiki](http://wiki.github.com/intridea/oauth2/examples).
|
55
|
+
|
56
|
+
JSON Parsing
|
57
|
+
------------
|
58
|
+
Because JSON has become the standard format of the OAuth 2.0 specification,
|
59
|
+
the <tt>oauth2</tt> gem contains a mode that will perform automatic parsing
|
60
|
+
of JSON response bodies, returning a hash instead of a string. To enable this
|
61
|
+
mode, simply add the <tt>:parse_json</tt> option to your client initialization:
|
62
|
+
|
63
|
+
client = OAuth2::Client.new(
|
64
|
+
'app_id',
|
65
|
+
'app_secret',
|
66
|
+
:site => 'https://example.com',
|
67
|
+
:parse_json => true,
|
68
|
+
)
|
69
|
+
|
70
|
+
# Obtain an access token using the client
|
71
|
+
token.get('/some/url.json') #=> {"some" => "hash"}
|
72
|
+
|
73
|
+
Testing
|
74
|
+
-------
|
75
|
+
To use the OAuth2 client for testing error conditions do:
|
76
|
+
|
77
|
+
my_client.raise_errors = false
|
78
|
+
|
79
|
+
It will then return the error status and response instead of raising an exception.
|
80
|
+
|
81
|
+
Note on Patches/Pull Requests
|
82
|
+
-----------------------------
|
83
|
+
1. Fork the project.
|
84
|
+
2. Create a topic branch.
|
85
|
+
3. Implement your feature or bug fix.
|
86
|
+
4. Add documentation for your feature or bug fix.
|
87
|
+
5. Add specs for your feature or bug fix.
|
88
|
+
6. Run <tt>bundle exec rake spec</tt>. If your changes are not 100% covered, go back to step 5.
|
89
|
+
7. Commit and push your changes.
|
90
|
+
8. Submit a pull request. Please do not include changes to the [gemspec](https://github.com/intridea/oauth2/blob/master/oauth2.gemspec), [version](https://github.com/intridea/oauth2/blob/master/lib/oauth2/version.rb), or [changelog](https://github.com/intridea/oauth2/blob/master/CHANGELOG.md) file. (If you want to create your own version for some reason, please do so in a separate commit.)
|
91
|
+
|
92
|
+
Copyright
|
93
|
+
---------
|
94
|
+
Copyright (c) 2011 Intridea, Inc. and Michael Bleigh.
|
95
|
+
See [LICENSE](https://github.com/intridea/oauth2/blob/master/LICENSE.md) for details.
|
data/lib/oauth2.rb
CHANGED
data/lib/oauth2/access_token.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module OAuth2
|
2
2
|
class AccessToken
|
3
|
-
attr_reader :client, :token, :refresh_token, :expires_in, :expires_at
|
3
|
+
attr_reader :client, :token, :refresh_token, :expires_in, :expires_at, :params
|
4
4
|
attr_accessor :token_param
|
5
5
|
|
6
|
-
def initialize(client, token, refresh_token
|
6
|
+
def initialize(client, token, refresh_token=nil, expires_in=nil, params={})
|
7
7
|
@client = client
|
8
8
|
@token = token.to_s
|
9
9
|
@refresh_token = refresh_token.to_s
|
@@ -26,25 +26,25 @@ module OAuth2
|
|
26
26
|
expires? && expires_at < Time.now
|
27
27
|
end
|
28
28
|
|
29
|
-
def request(verb, path, params
|
29
|
+
def request(verb, path, params={}, headers={})
|
30
30
|
params = params.merge token_param => @token
|
31
31
|
headers = headers.merge 'Authorization' => "OAuth #{@token}"
|
32
32
|
@client.request(verb, path, params, headers)
|
33
33
|
end
|
34
34
|
|
35
|
-
def get(path, params
|
35
|
+
def get(path, params={}, headers={})
|
36
36
|
request(:get, path, params, headers)
|
37
37
|
end
|
38
38
|
|
39
|
-
def post(path, params
|
39
|
+
def post(path, params={}, headers={})
|
40
40
|
request(:post, path, params, headers)
|
41
41
|
end
|
42
42
|
|
43
|
-
def put(path, params
|
43
|
+
def put(path, params={}, headers={})
|
44
44
|
request(:put, path, params, headers)
|
45
45
|
end
|
46
46
|
|
47
|
-
def delete(path, params
|
47
|
+
def delete(path, params={}, headers={})
|
48
48
|
request(:delete, path, params, headers)
|
49
49
|
end
|
50
50
|
end
|
data/lib/oauth2/client.rb
CHANGED
@@ -2,7 +2,7 @@ require 'faraday'
|
|
2
2
|
|
3
3
|
module OAuth2
|
4
4
|
class Client
|
5
|
-
attr_accessor :id, :secret, :site, :connection, :options
|
5
|
+
attr_accessor :id, :secret, :site, :connection, :options, :raise_errors
|
6
6
|
attr_writer :json
|
7
7
|
|
8
8
|
# Instantiate a new OAuth 2.0 client using the
|
@@ -17,32 +17,41 @@ module OAuth2
|
|
17
17
|
# <tt>:access_token_path</tt> :: Specify the path to the access token endpoint.
|
18
18
|
# <tt>:access_token_url</tt> :: Specify the full URL of the access token endpoint.
|
19
19
|
# <tt>:parse_json</tt> :: If true, <tt>application/json</tt> responses will be automatically parsed.
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
self.options
|
26
|
-
|
27
|
-
|
20
|
+
# <tt>:ssl</tt> :: Specify SSL options for the connection.
|
21
|
+
# <tt>:adapter</tt> :: The name of the Faraday::Adapter::* class to use, e.g. :net_http. To pass arguments
|
22
|
+
# to the adapter pass an array here, e.g. [:action_dispatch, my_test_session]
|
23
|
+
# <tt>:raise_errors</tt> :: Default true. When false it will then return the error status and response instead of raising an exception.
|
24
|
+
def initialize(client_id, client_secret, opts={})
|
25
|
+
self.options = opts.dup
|
26
|
+
adapter = self.options.delete(:adapter)
|
27
|
+
ssl_opts = self.options.delete(:ssl) || {}
|
28
|
+
connection_opts = ssl_opts ? {:ssl => ssl_opts} : {}
|
29
|
+
self.id = client_id
|
30
|
+
self.secret = client_secret
|
31
|
+
self.site = self.options.delete(:site) if self.options[:site]
|
32
|
+
self.connection = Faraday::Connection.new(site, connection_opts)
|
33
|
+
self.json = self.options.delete(:parse_json)
|
34
|
+
self.raise_errors = !(self.options.delete(:raise_errors) == false)
|
28
35
|
|
29
36
|
if adapter && adapter != :test
|
30
|
-
connection.build
|
37
|
+
connection.build do |b|
|
38
|
+
b.adapter(*[adapter].flatten)
|
39
|
+
end
|
31
40
|
end
|
32
41
|
end
|
33
42
|
|
34
|
-
def authorize_url(params
|
43
|
+
def authorize_url(params=nil)
|
35
44
|
path = options[:authorize_url] || options[:authorize_path] || "/oauth/authorize"
|
36
45
|
connection.build_url(path, params).to_s
|
37
46
|
end
|
38
47
|
|
39
|
-
def access_token_url(params
|
48
|
+
def access_token_url(params=nil)
|
40
49
|
path = options[:access_token_url] || options[:access_token_path] || "/oauth/access_token"
|
41
50
|
connection.build_url(path, params).to_s
|
42
51
|
end
|
43
52
|
|
44
53
|
# Makes a request relative to the specified site root.
|
45
|
-
def request(verb, url, params
|
54
|
+
def request(verb, url, params={}, headers={})
|
46
55
|
if verb == :get
|
47
56
|
resp = connection.run_request(verb, url, nil, headers) do |req|
|
48
57
|
req.params.update(params)
|
@@ -51,26 +60,37 @@ module OAuth2
|
|
51
60
|
resp = connection.run_request(verb, url, params, headers)
|
52
61
|
end
|
53
62
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
return
|
63
|
+
if raise_errors
|
64
|
+
case resp.status
|
65
|
+
when 200...299
|
66
|
+
return response_for(resp)
|
67
|
+
when 401
|
68
|
+
e = OAuth2::AccessDenied.new("Received HTTP 401 during request.")
|
69
|
+
e.response = resp
|
70
|
+
raise e
|
58
71
|
else
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
else
|
66
|
-
e = OAuth2::HTTPError.new("Received HTTP #{resp.status} during request.")
|
67
|
-
e.response = resp
|
68
|
-
raise e
|
72
|
+
e = OAuth2::HTTPError.new("Received HTTP #{resp.status} during request.")
|
73
|
+
e.response = resp
|
74
|
+
raise e
|
75
|
+
end
|
76
|
+
else
|
77
|
+
response_for resp
|
69
78
|
end
|
70
79
|
end
|
71
80
|
|
72
81
|
def json?; !!@json end
|
73
82
|
|
74
83
|
def web_server; OAuth2::Strategy::WebServer.new(self) end
|
84
|
+
def password; OAuth2::Strategy::Password.new(self) end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def response_for(resp)
|
89
|
+
if json?
|
90
|
+
return ResponseObject.from(resp)
|
91
|
+
else
|
92
|
+
return ResponseString.new(resp)
|
93
|
+
end
|
94
|
+
end
|
75
95
|
end
|
76
96
|
end
|
data/lib/oauth2/strategy/base.rb
CHANGED
@@ -5,20 +5,20 @@ module OAuth2
|
|
5
5
|
@client = client
|
6
6
|
end
|
7
7
|
|
8
|
-
def authorize_url(options
|
8
|
+
def authorize_url(options={}) #:nodoc:
|
9
9
|
@client.authorize_url(authorize_params(options))
|
10
10
|
end
|
11
11
|
|
12
|
-
def authorize_params(options
|
12
|
+
def authorize_params(options={}) #:nodoc:
|
13
13
|
options = options.inject({}){|h, (k, v)| h[k.to_s] = v; h}
|
14
14
|
{'client_id' => @client.id}.merge(options)
|
15
15
|
end
|
16
16
|
|
17
|
-
def access_token_url(options
|
17
|
+
def access_token_url(options={})
|
18
18
|
@client.access_token_url(access_token_params(options))
|
19
19
|
end
|
20
20
|
|
21
|
-
def access_token_params(options
|
21
|
+
def access_token_params(options={})
|
22
22
|
{
|
23
23
|
'client_id' => @client.id,
|
24
24
|
'client_secret' => @client.secret
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
module OAuth2
|
4
|
+
module Strategy
|
5
|
+
class Password < Base
|
6
|
+
def authorize_url
|
7
|
+
raise NotImplementedError, "The authorization endpoint is not used in this strategy"
|
8
|
+
end
|
9
|
+
# Retrieve an access token given the specified validation code.
|
10
|
+
# Note that you must also provide a <tt>:redirect_uri</tt> option
|
11
|
+
# in order to successfully verify your request for most OAuth 2.0
|
12
|
+
# endpoints.
|
13
|
+
def get_access_token(username, password, options={})
|
14
|
+
response = @client.request(:post, @client.access_token_url, access_token_params(username, password, options))
|
15
|
+
|
16
|
+
params = MultiJson.decode(response) rescue nil
|
17
|
+
# the ActiveSupport JSON parser won't cause an exception when
|
18
|
+
# given a formencoded string, so make sure that it was
|
19
|
+
# actually parsed in an Hash. This covers even the case where
|
20
|
+
# it caused an exception since it'll still be nil.
|
21
|
+
params = Rack::Utils.parse_query(response) unless params.is_a? Hash
|
22
|
+
|
23
|
+
access = params.delete('access_token')
|
24
|
+
refresh = params.delete('refresh_token')
|
25
|
+
expires_in = params.delete('expires_in')
|
26
|
+
OAuth2::AccessToken.new(@client, access, refresh, expires_in, params)
|
27
|
+
end
|
28
|
+
|
29
|
+
def access_token_params(username, password, options={}) #:nodoc:
|
30
|
+
super(options).merge({
|
31
|
+
'grant_type' => 'password',
|
32
|
+
'username' => username,
|
33
|
+
'password' => password
|
34
|
+
})
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -3,7 +3,7 @@ require 'multi_json'
|
|
3
3
|
module OAuth2
|
4
4
|
module Strategy
|
5
5
|
class WebServer < Base
|
6
|
-
def authorize_params(options
|
6
|
+
def authorize_params(options={}) #:nodoc:
|
7
7
|
super(options).merge('response_type' => 'code')
|
8
8
|
end
|
9
9
|
|
@@ -11,36 +11,31 @@ module OAuth2
|
|
11
11
|
# Note that you must also provide a <tt>:redirect_uri</tt> option
|
12
12
|
# in order to successfully verify your request for most OAuth 2.0
|
13
13
|
# endpoints.
|
14
|
-
def get_access_token(code, options
|
15
|
-
response = @client.request(:
|
14
|
+
def get_access_token(code, options={})
|
15
|
+
response = @client.request(:get, @client.access_token_url, access_token_params(code, options))
|
16
16
|
|
17
17
|
if response.is_a? Hash
|
18
|
-
params=response
|
18
|
+
params = response
|
19
19
|
else
|
20
|
-
params
|
20
|
+
params = MultiJson.decode(response) rescue nil
|
21
21
|
# the ActiveSupport JSON parser won't cause an exception when
|
22
22
|
# given a formencoded string, so make sure that it was
|
23
23
|
# actually parsed in an Hash. This covers even the case where
|
24
24
|
# it caused an exception since it'll still be nil.
|
25
|
-
params
|
25
|
+
params = Rack::Utils.parse_query(response) unless params.is_a? Hash
|
26
26
|
end
|
27
27
|
|
28
|
-
access
|
29
|
-
refresh
|
30
|
-
|
28
|
+
access = params.delete('access_token')
|
29
|
+
refresh = params.delete('refresh_token')
|
30
|
+
# params['expires'] is only for Facebook
|
31
|
+
expires_in = params.delete('expires_in') || params.delete('expires')
|
31
32
|
OAuth2::AccessToken.new(@client, access, refresh, expires_in, params)
|
32
33
|
end
|
33
34
|
|
34
|
-
|
35
|
-
def access_token(*args)
|
36
|
-
warn '[DEPRECATED] OAuth2::Strategy::WebServer#access_token is deprecated, use #get_access_token instead. Will be removed in 0.1.0'
|
37
|
-
get_access_token(*args)
|
38
|
-
end
|
39
|
-
|
40
|
-
def access_token_params(code, options = {}) #:nodoc:
|
35
|
+
def access_token_params(code, options={}) #:nodoc:
|
41
36
|
super(options).merge({
|
42
37
|
'grant_type' => 'authorization_code',
|
43
|
-
'code' => code
|
38
|
+
'code' => code,
|
44
39
|
})
|
45
40
|
end
|
46
41
|
end
|
data/lib/oauth2/version.rb
CHANGED
data/oauth2.gemspec
CHANGED
@@ -9,8 +9,6 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.description = %q{A Ruby wrapper for the OAuth 2.0 protocol built with a similar style to the original OAuth gem.}
|
10
10
|
s.summary = %q{A Ruby wrapper for the OAuth 2.0 protocol.}
|
11
11
|
s.email = "michael@intridea.com"
|
12
|
-
s.extra_rdoc_files = ["LICENSE", "README.rdoc"]
|
13
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
14
12
|
s.homepage = "http://github.com/intridea/oauth2"
|
15
13
|
s.require_paths = ["lib"]
|
16
14
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
@@ -5,18 +5,18 @@ describe OAuth2::AccessToken do
|
|
5
5
|
cli = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com')
|
6
6
|
cli.connection.build do |b|
|
7
7
|
b.adapter :test do |stub|
|
8
|
-
stub.get('/client?oauth_token=monkey') {
|
9
|
-
stub.post('/client') {
|
10
|
-
stub.put('/client') {
|
11
|
-
stub.delete('/client') {
|
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
12
|
end
|
13
13
|
end
|
14
14
|
cli
|
15
15
|
end
|
16
16
|
|
17
|
-
let(:token)
|
17
|
+
let(:token) {'monkey'}
|
18
18
|
|
19
|
-
subject {
|
19
|
+
subject {OAuth2::AccessToken.new(client, token)}
|
20
20
|
|
21
21
|
describe '#initialize' do
|
22
22
|
it 'should assign client and token' do
|
@@ -24,6 +24,12 @@ describe OAuth2::AccessToken do
|
|
24
24
|
subject.token.should == token
|
25
25
|
end
|
26
26
|
|
27
|
+
it 'should assign extra params' do
|
28
|
+
target = OAuth2::AccessToken.new(client, token, nil, nil, {'foo' => 'bar'})
|
29
|
+
target.params.should include('foo')
|
30
|
+
target.params['foo'].should == 'bar'
|
31
|
+
end
|
32
|
+
|
27
33
|
it "makes GET requests with access token" do
|
28
34
|
subject.send(:get, 'client').should == 'get'
|
29
35
|
end
|
@@ -51,7 +57,7 @@ describe OAuth2::AccessToken do
|
|
51
57
|
Time.stub!(:now).and_return(@now)
|
52
58
|
end
|
53
59
|
|
54
|
-
subject{
|
60
|
+
subject{OAuth2::AccessToken.new(client, token, 'abaca', 600)}
|
55
61
|
|
56
62
|
it 'should be a time representation of #expires_in' do
|
57
63
|
subject.expires_at.should == (@now + 600)
|
data/spec/oauth2/client_spec.rb
CHANGED
@@ -5,10 +5,10 @@ describe OAuth2::Client do
|
|
5
5
|
cli = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com')
|
6
6
|
cli.connection.build do |b|
|
7
7
|
b.adapter :test do |stub|
|
8
|
-
stub.get('/success') {
|
9
|
-
stub.get('/unauthorized') {
|
10
|
-
stub.get('/error') {
|
11
|
-
stub.get('/json') {
|
8
|
+
stub.get('/success') {|env| [200, {'Content-Type' => 'text/awesome'}, 'yay']}
|
9
|
+
stub.get('/unauthorized') {|env| [401, {'Content-Type' => 'text/plain'}, 'not authorized']}
|
10
|
+
stub.get('/error') {|env| [500, {}, '']}
|
11
|
+
stub.get('/json') {|env| [200, {'Content-Type' => 'application/json; charset=utf8'}, '{"abc":"def"}']}
|
12
12
|
end
|
13
13
|
end
|
14
14
|
cli
|
@@ -27,6 +27,33 @@ describe OAuth2::Client do
|
|
27
27
|
it 'should assign Faraday::Connection#host' do
|
28
28
|
subject.connection.host.should == 'api.example.com'
|
29
29
|
end
|
30
|
+
|
31
|
+
it 'should leave Faraday::Connection#ssl unset' do
|
32
|
+
subject.connection.ssl.should == {}
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should be able to pass parameters to the adapter, e.g. Faraday::Adapter::ActionDispatch" do
|
36
|
+
connection = stub('connection')
|
37
|
+
Faraday::Connection.stub(:new => connection)
|
38
|
+
session = stub('session', :to_ary => nil)
|
39
|
+
builder = stub('builder')
|
40
|
+
connection.stub(:build).and_yield(builder)
|
41
|
+
|
42
|
+
builder.should_receive(:adapter).with(:action_dispatch, session)
|
43
|
+
|
44
|
+
OAuth2::Client.new('abc', 'def', :adapter => [:action_dispatch, session])
|
45
|
+
end
|
46
|
+
|
47
|
+
it "defaults raise_errors to true" do
|
48
|
+
subject.raise_errors.should be_true
|
49
|
+
end
|
50
|
+
|
51
|
+
it "allows true/false for raise_errors option" do
|
52
|
+
client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :raise_errors => false)
|
53
|
+
client.raise_errors.should be_false
|
54
|
+
client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :raise_errors => true)
|
55
|
+
client.raise_errors.should be_true
|
56
|
+
end
|
30
57
|
end
|
31
58
|
|
32
59
|
%w(authorize access_token).each do |path_type|
|
@@ -55,12 +82,21 @@ describe OAuth2::Client do
|
|
55
82
|
response.headers.should == {'Content-Type' => 'text/awesome'}
|
56
83
|
end
|
57
84
|
|
85
|
+
it "returns ResponseString on error if raise_errors is false" do
|
86
|
+
subject.raise_errors = false
|
87
|
+
response = subject.request(:get, '/unauthorized', {}, {})
|
88
|
+
|
89
|
+
response.should == 'not authorized'
|
90
|
+
response.status.should == 401
|
91
|
+
response.headers.should == {'Content-Type' => 'text/plain'}
|
92
|
+
end
|
93
|
+
|
58
94
|
it "raises OAuth2::AccessDenied on 401 response" do
|
59
|
-
lambda {
|
95
|
+
lambda {subject.request(:get, '/unauthorized', {}, {})}.should raise_error(OAuth2::AccessDenied)
|
60
96
|
end
|
61
97
|
|
62
98
|
it "raises OAuth2::HTTPError on error response" do
|
63
|
-
lambda {
|
99
|
+
lambda {subject.request(:get, '/error', {}, {})}.should raise_error(OAuth2::HTTPError)
|
64
100
|
end
|
65
101
|
end
|
66
102
|
|
@@ -95,4 +131,18 @@ describe OAuth2::Client do
|
|
95
131
|
subject.json = false
|
96
132
|
end
|
97
133
|
end
|
134
|
+
|
135
|
+
context 'with SSL options' do
|
136
|
+
subject do
|
137
|
+
cli = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :ssl => {:ca_file => 'foo.pem'})
|
138
|
+
cli.connection.build do |b|
|
139
|
+
b.adapter :test
|
140
|
+
end
|
141
|
+
cli
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'should pass the SSL options along to Faraday::Connection#ssl' do
|
145
|
+
subject.connection.ssl.should == {:ca_file => 'foo.pem'}
|
146
|
+
end
|
147
|
+
end
|
98
148
|
end
|
@@ -2,6 +2,6 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe OAuth2::Strategy::Base do
|
4
4
|
it 'should initialize with a Client' do
|
5
|
-
lambda{
|
5
|
+
lambda{OAuth2::Strategy::Base.new(OAuth2::Client.new('abc', 'def'))}.should_not raise_error
|
6
6
|
end
|
7
7
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OAuth2::Strategy::Password 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']
|
12
|
+
when "json"
|
13
|
+
[200, {}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}']
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
cli
|
19
|
+
end
|
20
|
+
subject {client.password}
|
21
|
+
|
22
|
+
describe "#authorize_url" do
|
23
|
+
it "should raise NotImplementedError" do
|
24
|
+
lambda {subject.authorize_url}.should raise_error(NotImplementedError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
%w(json formencoded).each do |mode|
|
29
|
+
describe "#get_access_token (#{mode})" do
|
30
|
+
before do
|
31
|
+
@mode = mode
|
32
|
+
@access = subject.get_access_token('username', 'password')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'returns AccessToken with same Client' do
|
36
|
+
@access.client.should == client
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns AccessToken with #token' do
|
40
|
+
@access.token.should == 'salmon'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'returns AccessToken with #refresh_token' do
|
44
|
+
@access.refresh_token.should == 'trout'
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'returns AccessToken with #expires_in' do
|
48
|
+
@access.expires_in.should == 600
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns AccessToken with #expires_at' do
|
52
|
+
@access.expires_at.should be_kind_of(Time)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -5,21 +5,21 @@ describe OAuth2::Strategy::WebServer do
|
|
5
5
|
cli = OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com')
|
6
6
|
cli.connection.build do |b|
|
7
7
|
b.adapter :test do |stub|
|
8
|
-
stub.
|
8
|
+
stub.get('/oauth/access_token?client_id=abc&client_secret=def&code=sushi&grant_type=authorization_code') do |env|
|
9
9
|
case @mode
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
+
when "from_facebook"
|
15
|
+
[200, {}, 'expires=600&access_token=salmon&refresh_token=trout&extra_param=steve']
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
20
20
|
cli
|
21
21
|
end
|
22
|
-
subject
|
22
|
+
subject{client.web_server}
|
23
23
|
|
24
24
|
describe '#authorize_url' do
|
25
25
|
it 'should include the client_id' do
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: oauth2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.3.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Michael Bleigh
|
@@ -10,8 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-04-
|
14
|
-
default_executable:
|
13
|
+
date: 2011-04-08 00:00:00 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: faraday
|
@@ -96,39 +95,40 @@ executables: []
|
|
96
95
|
|
97
96
|
extensions: []
|
98
97
|
|
99
|
-
extra_rdoc_files:
|
100
|
-
|
101
|
-
- README.rdoc
|
98
|
+
extra_rdoc_files: []
|
99
|
+
|
102
100
|
files:
|
103
101
|
- .document
|
104
102
|
- .gemtest
|
105
103
|
- .gitignore
|
106
104
|
- .rspec
|
107
|
-
-
|
105
|
+
- .travis.yml
|
106
|
+
- CHANGELOG.md
|
108
107
|
- Gemfile
|
109
|
-
- LICENSE
|
110
|
-
- README.
|
108
|
+
- LICENSE.md
|
109
|
+
- README.md
|
111
110
|
- Rakefile
|
112
111
|
- lib/oauth2.rb
|
113
112
|
- lib/oauth2/access_token.rb
|
114
113
|
- lib/oauth2/client.rb
|
115
114
|
- lib/oauth2/response_object.rb
|
116
115
|
- lib/oauth2/strategy/base.rb
|
116
|
+
- lib/oauth2/strategy/password.rb
|
117
117
|
- lib/oauth2/strategy/web_server.rb
|
118
118
|
- lib/oauth2/version.rb
|
119
119
|
- oauth2.gemspec
|
120
120
|
- spec/oauth2/access_token_spec.rb
|
121
121
|
- spec/oauth2/client_spec.rb
|
122
122
|
- spec/oauth2/strategy/base_spec.rb
|
123
|
+
- spec/oauth2/strategy/password_spec.rb
|
123
124
|
- spec/oauth2/strategy/web_server_spec.rb
|
124
125
|
- spec/spec_helper.rb
|
125
|
-
has_rdoc: true
|
126
126
|
homepage: http://github.com/intridea/oauth2
|
127
127
|
licenses: []
|
128
128
|
|
129
129
|
post_install_message:
|
130
|
-
rdoc_options:
|
131
|
-
|
130
|
+
rdoc_options: []
|
131
|
+
|
132
132
|
require_paths:
|
133
133
|
- lib
|
134
134
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -146,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
146
146
|
requirements: []
|
147
147
|
|
148
148
|
rubyforge_project:
|
149
|
-
rubygems_version: 1.
|
149
|
+
rubygems_version: 1.7.2
|
150
150
|
signing_key:
|
151
151
|
specification_version: 3
|
152
152
|
summary: A Ruby wrapper for the OAuth 2.0 protocol.
|
@@ -154,5 +154,7 @@ test_files:
|
|
154
154
|
- spec/oauth2/access_token_spec.rb
|
155
155
|
- spec/oauth2/client_spec.rb
|
156
156
|
- spec/oauth2/strategy/base_spec.rb
|
157
|
+
- spec/oauth2/strategy/password_spec.rb
|
157
158
|
- spec/oauth2/strategy/web_server_spec.rb
|
158
159
|
- spec/spec_helper.rb
|
160
|
+
has_rdoc:
|
data/README.rdoc
DELETED
@@ -1,80 +0,0 @@
|
|
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.
|