oauth2 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +19 -21
- data/lib/oauth2.rb +2 -1
- data/lib/oauth2/access_token.rb +3 -1
- data/lib/oauth2/client.rb +11 -2
- data/lib/oauth2/response_object.rb +1 -1
- data/lib/oauth2/strategy/base.rb +9 -0
- data/lib/oauth2/strategy/web_server.rb +24 -9
- data/lib/oauth2/version.rb +1 -1
- data/spec/oauth2/access_token_spec.rb +14 -7
- data/spec/oauth2/client_spec.rb +22 -2
- data/spec/oauth2/strategy/web_server_spec.rb +86 -22
- data/spec/spec_helper.rb +4 -0
- metadata +2 -2
data/CHANGELOG.md
CHANGED
@@ -1,22 +1,20 @@
|
|
1
|
-
|
2
|
-
----------------------
|
3
|
-
* Handle ActiveSupport JSON case where incompatible string does not raise an error. (via Flameeyes)
|
4
|
-
* Depend on latest version of MultiJSON.
|
1
|
+
# CHANGELOG
|
5
2
|
|
6
|
-
0.0
|
7
|
-
|
8
|
-
*
|
9
|
-
*
|
10
|
-
|
11
|
-
0.0.
|
12
|
-
|
13
|
-
*
|
14
|
-
|
15
|
-
0.0.
|
16
|
-
|
17
|
-
*
|
18
|
-
|
19
|
-
0.0.
|
20
|
-
|
21
|
-
*
|
22
|
-
*
|
3
|
+
* [0.4.0 - April 20, 2011](https://github.com/intridea/oauth2/compare/v0.3.0...v0.4.0)
|
4
|
+
* [0.3.0 - April 8, 2011](https://github.com/intridea/oauth2/compare/v0.2.0...v0.3.0)
|
5
|
+
* [0.2.0 - April 1, 2011](https://github.com/intridea/oauth2/compare/v0.1.1...v0.2.0)
|
6
|
+
* [0.1.1 - January 12, 2011](https://github.com/intridea/oauth2/compare/v0.1.0...v0.1.1)
|
7
|
+
* [0.1.0 - October 13, 2010](https://github.com/intridea/oauth2/compare/v0.0.13...v0.1.0)
|
8
|
+
* [0.0.13 - August 17, 2010](https://github.com/intridea/oauth2/compare/v0.0.12...v0.0.13)
|
9
|
+
* [0.0.12 - August 17, 2010](https://github.com/intridea/oauth2/compare/v0.0.11...v0.0.12)
|
10
|
+
* [0.0.11 - August 17, 2010](https://github.com/intridea/oauth2/compare/v0.0.10...v0.0.11)
|
11
|
+
* [0.0.10 - June 19, 2010](https://github.com/intridea/oauth2/compare/v0.0.9...v0.0.10)
|
12
|
+
* [0.0.9 - June 18, 2010](https://github.com/intridea/oauth2/compare/v0.0.8...v0.0.9)
|
13
|
+
* [0.0.8 - April 27, 2010](https://github.com/intridea/oauth2/compare/v0.0.7...v0.0.8)
|
14
|
+
* [0.0.7 - April 27, 2010](https://github.com/intridea/oauth2/compare/v0.0.6...v0.0.7)
|
15
|
+
* [0.0.6 - April 25, 2010](https://github.com/intridea/oauth2/compare/v0.0.5...v0.0.6)
|
16
|
+
* [0.0.5 - April 23, 2010](https://github.com/intridea/oauth2/compare/v0.0.4...v0.0.5)
|
17
|
+
* [0.0.4 - April 22, 2010](https://github.com/intridea/oauth2/compare/v0.0.3...v0.0.4)
|
18
|
+
* [0.0.3 - April 22, 2010](https://github.com/intridea/oauth2/compare/v0.0.2...v0.0.3)
|
19
|
+
* [0.0.2 - April 22, 2010](https://github.com/intridea/oauth2/compare/v0.0.1...v0.0.2)
|
20
|
+
* [0.0.1 - April 22, 2010](https://github.com/intridea/oauth2/compare/311d9f42e52b832119170d90e818f0f0b0078851...v0.0.1)
|
data/lib/oauth2.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module OAuth2
|
2
2
|
class ErrorWithResponse < StandardError; attr_accessor :response end
|
3
3
|
class AccessDenied < ErrorWithResponse; end
|
4
|
+
class Conflict < ErrorWithResponse; end
|
4
5
|
class HTTPError < ErrorWithResponse; end
|
5
6
|
end
|
6
7
|
|
@@ -9,4 +10,4 @@ require 'oauth2/strategy/base'
|
|
9
10
|
require 'oauth2/strategy/web_server'
|
10
11
|
require 'oauth2/strategy/password'
|
11
12
|
require 'oauth2/access_token'
|
12
|
-
require 'oauth2/response_object'
|
13
|
+
require 'oauth2/response_object'
|
data/lib/oauth2/access_token.rb
CHANGED
@@ -27,7 +27,9 @@ module OAuth2
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def request(verb, path, params={}, headers={})
|
30
|
-
|
30
|
+
if params.instance_of?(Hash)
|
31
|
+
params = params.merge token_param => @token
|
32
|
+
end
|
31
33
|
headers = headers.merge 'Authorization' => "OAuth #{@token}"
|
32
34
|
@client.request(verb, path, params, headers)
|
33
35
|
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, :raise_errors
|
5
|
+
attr_accessor :id, :secret, :site, :connection, :options, :raise_errors, :token_method
|
6
6
|
attr_writer :json
|
7
7
|
|
8
8
|
# Instantiate a new OAuth 2.0 client using the
|
@@ -15,6 +15,8 @@ module OAuth2
|
|
15
15
|
# <tt>:authorize_path</tt> :: Specify the path to the authorization endpoint.
|
16
16
|
# <tt>:authorize_url</tt> :: Specify a full URL of the authorization endpoint.
|
17
17
|
# <tt>:access_token_path</tt> :: Specify the path to the access token endpoint.
|
18
|
+
# <tt>:access_token_method</tt> :: Specify the method to use for token endpoints, can be :get or :post
|
19
|
+
# (note: for Facebook this should be :get and for Google this should be :post)
|
18
20
|
# <tt>:access_token_url</tt> :: Specify the full URL of the access token endpoint.
|
19
21
|
# <tt>:parse_json</tt> :: If true, <tt>application/json</tt> responses will be automatically parsed.
|
20
22
|
# <tt>:ssl</tt> :: Specify SSL options for the connection.
|
@@ -23,6 +25,7 @@ module OAuth2
|
|
23
25
|
# <tt>:raise_errors</tt> :: Default true. When false it will then return the error status and response instead of raising an exception.
|
24
26
|
def initialize(client_id, client_secret, opts={})
|
25
27
|
self.options = opts.dup
|
28
|
+
self.token_method = self.options.delete(:access_token_method) || :get
|
26
29
|
adapter = self.options.delete(:adapter)
|
27
30
|
ssl_opts = self.options.delete(:ssl) || {}
|
28
31
|
connection_opts = ssl_opts ? {:ssl => ssl_opts} : {}
|
@@ -52,7 +55,7 @@ module OAuth2
|
|
52
55
|
|
53
56
|
# Makes a request relative to the specified site root.
|
54
57
|
def request(verb, url, params={}, headers={})
|
55
|
-
if verb == :get
|
58
|
+
if (verb == :get) || (verb == :delete)
|
56
59
|
resp = connection.run_request(verb, url, nil, headers) do |req|
|
57
60
|
req.params.update(params)
|
58
61
|
end
|
@@ -64,10 +67,16 @@ module OAuth2
|
|
64
67
|
case resp.status
|
65
68
|
when 200...299
|
66
69
|
return response_for(resp)
|
70
|
+
when 302
|
71
|
+
return request(verb, resp.headers['location'], params, headers)
|
67
72
|
when 401
|
68
73
|
e = OAuth2::AccessDenied.new("Received HTTP 401 during request.")
|
69
74
|
e.response = resp
|
70
75
|
raise e
|
76
|
+
when 409
|
77
|
+
e = OAuth2::Conflict.new("Received HTTP 409 during request.")
|
78
|
+
e.response = resp
|
79
|
+
raise e
|
71
80
|
else
|
72
81
|
e = OAuth2::HTTPError.new("Received HTTP #{resp.status} during request.")
|
73
82
|
e.response = resp
|
data/lib/oauth2/strategy/base.rb
CHANGED
@@ -19,6 +19,15 @@ module OAuth2
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def access_token_params(options={})
|
22
|
+
return default_params(options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def refresh_token_params(options={})
|
26
|
+
return default_params(options)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def default_params(options={})
|
22
31
|
{
|
23
32
|
'client_id' => @client.id,
|
24
33
|
'client_secret' => @client.secret
|
@@ -12,8 +12,30 @@ module OAuth2
|
|
12
12
|
# in order to successfully verify your request for most OAuth 2.0
|
13
13
|
# endpoints.
|
14
14
|
def get_access_token(code, options={})
|
15
|
-
response = @client.request(
|
15
|
+
response = @client.request(@client.token_method, @client.access_token_url, access_token_params(code, options))
|
16
|
+
parse_response(response)
|
17
|
+
end
|
18
|
+
|
19
|
+
def refresh_access_token(refresh_token, options={})
|
20
|
+
response = @client.request(@client.token_method, @client.access_token_url, refresh_token_params(refresh_token, options))
|
21
|
+
parse_response(response, refresh_token)
|
22
|
+
end
|
16
23
|
|
24
|
+
def access_token_params(code, options={}) #:nodoc:
|
25
|
+
super(options).merge({
|
26
|
+
'grant_type' => 'authorization_code',
|
27
|
+
'code' => code,
|
28
|
+
})
|
29
|
+
end
|
30
|
+
|
31
|
+
def refresh_token_params(refresh_token, options={}) #:nodoc:
|
32
|
+
super(options).merge({
|
33
|
+
'grant_type' => 'refresh_token',
|
34
|
+
'refresh_token' => refresh_token,
|
35
|
+
})
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_response(response, refresh_token = nil)
|
17
39
|
if response.is_a? Hash
|
18
40
|
params = response
|
19
41
|
else
|
@@ -26,18 +48,11 @@ module OAuth2
|
|
26
48
|
end
|
27
49
|
|
28
50
|
access = params.delete('access_token')
|
29
|
-
refresh = params.delete('refresh_token')
|
51
|
+
refresh = params.delete('refresh_token') || refresh_token
|
30
52
|
# params['expires'] is only for Facebook
|
31
53
|
expires_in = params.delete('expires_in') || params.delete('expires')
|
32
54
|
OAuth2::AccessToken.new(@client, access, refresh, expires_in, params)
|
33
55
|
end
|
34
|
-
|
35
|
-
def access_token_params(code, options={}) #:nodoc:
|
36
|
-
super(options).merge({
|
37
|
-
'grant_type' => 'authorization_code',
|
38
|
-
'code' => code,
|
39
|
-
})
|
40
|
-
end
|
41
56
|
end
|
42
57
|
end
|
43
58
|
end
|
data/lib/oauth2/version.rb
CHANGED
@@ -5,10 +5,11 @@ 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.
|
11
|
-
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.get('/empty_get?oauth_token=monkey') {|env| [204, {}, nil]}
|
11
|
+
stub.put('/client') {|env| [200, {}, 'oauth_token=' << env[:body]['oauth_token']]}
|
12
|
+
stub.delete('/client?oauth_token=monkey') {|env| [200, {}, 'delete']}
|
12
13
|
end
|
13
14
|
end
|
14
15
|
cli
|
@@ -30,15 +31,21 @@ describe OAuth2::AccessToken do
|
|
30
31
|
target.params['foo'].should == 'bar'
|
31
32
|
end
|
32
33
|
|
33
|
-
|
34
|
-
|
34
|
+
%w(get delete).each do |http_method|
|
35
|
+
it "makes #{http_method.upcase} requests with access token" do
|
36
|
+
subject.send(http_method.to_sym, 'client').should == http_method
|
37
|
+
end
|
35
38
|
end
|
36
39
|
|
37
|
-
%w(post put
|
40
|
+
%w(post put).each do |http_method|
|
38
41
|
it "makes #{http_method.upcase} requests with access token" do
|
39
42
|
subject.send(http_method.to_sym, 'client').should == 'oauth_token=monkey'
|
40
43
|
end
|
41
44
|
end
|
45
|
+
|
46
|
+
it "works with a null response body" do
|
47
|
+
subject.get('empty_get').should == ''
|
48
|
+
end
|
42
49
|
end
|
43
50
|
|
44
51
|
describe '#expires?' do
|
data/spec/oauth2/client_spec.rb
CHANGED
@@ -7,6 +7,8 @@ describe OAuth2::Client do
|
|
7
7
|
b.adapter :test do |stub|
|
8
8
|
stub.get('/success') {|env| [200, {'Content-Type' => 'text/awesome'}, 'yay']}
|
9
9
|
stub.get('/unauthorized') {|env| [401, {'Content-Type' => 'text/plain'}, 'not authorized']}
|
10
|
+
stub.get('/conflict') {|env| [409, {'Content-Type' => 'text/plain'}, 'not authorized']}
|
11
|
+
stub.get('/redirect') {|env| [302, {'Content-Type' => 'text/plain', 'location' => '/success' }, '']}
|
10
12
|
stub.get('/error') {|env| [500, {}, '']}
|
11
13
|
stub.get('/json') {|env| [200, {'Content-Type' => 'application/json; charset=utf8'}, '{"abc":"def"}']}
|
12
14
|
end
|
@@ -43,17 +45,24 @@ describe OAuth2::Client do
|
|
43
45
|
|
44
46
|
OAuth2::Client.new('abc', 'def', :adapter => [:action_dispatch, session])
|
45
47
|
end
|
46
|
-
|
48
|
+
|
47
49
|
it "defaults raise_errors to true" do
|
48
50
|
subject.raise_errors.should be_true
|
49
51
|
end
|
50
|
-
|
52
|
+
|
51
53
|
it "allows true/false for raise_errors option" do
|
52
54
|
client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :raise_errors => false)
|
53
55
|
client.raise_errors.should be_false
|
54
56
|
client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :raise_errors => true)
|
55
57
|
client.raise_errors.should be_true
|
56
58
|
end
|
59
|
+
|
60
|
+
it "allows get/post for access_token_method option" do
|
61
|
+
client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :access_token_method => :get)
|
62
|
+
client.token_method.should == :get
|
63
|
+
client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :access_token_method => :post)
|
64
|
+
client.token_method.should == :post
|
65
|
+
end
|
57
66
|
end
|
58
67
|
|
59
68
|
%w(authorize access_token).each do |path_type|
|
@@ -82,6 +91,13 @@ describe OAuth2::Client do
|
|
82
91
|
response.headers.should == {'Content-Type' => 'text/awesome'}
|
83
92
|
end
|
84
93
|
|
94
|
+
it "follows redirects properly" do
|
95
|
+
response = subject.request(:get, '/redirect', {}, {})
|
96
|
+
response.should == 'yay'
|
97
|
+
response.status.should == 200
|
98
|
+
response.headers.should == {'Content-Type' => 'text/awesome'}
|
99
|
+
end
|
100
|
+
|
85
101
|
it "returns ResponseString on error if raise_errors is false" do
|
86
102
|
subject.raise_errors = false
|
87
103
|
response = subject.request(:get, '/unauthorized', {}, {})
|
@@ -95,6 +111,10 @@ describe OAuth2::Client do
|
|
95
111
|
lambda {subject.request(:get, '/unauthorized', {}, {})}.should raise_error(OAuth2::AccessDenied)
|
96
112
|
end
|
97
113
|
|
114
|
+
it "raises OAuth2::Conflict on 409 response" do
|
115
|
+
lambda {subject.request(:get, '/conflict', {}, {})}.should raise_error(OAuth2::Conflict)
|
116
|
+
end
|
117
|
+
|
98
118
|
it "raises OAuth2::HTTPError on error response" do
|
99
119
|
lambda {subject.request(:get, '/error', {}, {})}.should raise_error(OAuth2::HTTPError)
|
100
120
|
end
|
@@ -15,6 +15,32 @@ describe OAuth2::Strategy::WebServer do
|
|
15
15
|
[200, {}, 'expires=600&access_token=salmon&refresh_token=trout&extra_param=steve']
|
16
16
|
end
|
17
17
|
end
|
18
|
+
stub.post('/oauth/access_token', { 'client_id' => 'abc', 'client_secret' => 'def', 'code' => 'sushi', 'grant_type' => 'authorization_code' }) do |env|
|
19
|
+
case @mode
|
20
|
+
when "formencoded"
|
21
|
+
[200, {}, 'expires_in=600&access_token=salmon&refresh_token=trout&extra_param=steve']
|
22
|
+
when "json"
|
23
|
+
[200, {}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout","extra_param":"steve"}']
|
24
|
+
when "from_facebook"
|
25
|
+
[200, {}, 'expires=600&access_token=salmon&refresh_token=trout&extra_param=steve']
|
26
|
+
end
|
27
|
+
end
|
28
|
+
stub.get('/oauth/access_token?client_id=abc&client_secret=def&grant_type=refresh_token&refresh_token=trout') do |env|
|
29
|
+
case @mode
|
30
|
+
when "formencoded"
|
31
|
+
[200, {}, 'expires_in=600&access_token=tuna']
|
32
|
+
when "json"
|
33
|
+
[200, {}, '{"expires_in":600,"access_token":"tuna"}']
|
34
|
+
end
|
35
|
+
end
|
36
|
+
stub.post('/oauth/access_token', { 'client_id' => 'abc', 'client_secret' => 'def', 'refresh_token' => 'trout', 'grant_type' => 'refresh_token' }) do |env|
|
37
|
+
case @mode
|
38
|
+
when "formencoded"
|
39
|
+
[200, {}, 'expires_in=600&access_token=tuna']
|
40
|
+
when "json"
|
41
|
+
[200, {}, '{"expires_in":600,"access_token":"tuna"}']
|
42
|
+
end
|
43
|
+
end
|
18
44
|
end
|
19
45
|
end
|
20
46
|
cli
|
@@ -38,35 +64,73 @@ describe OAuth2::Strategy::WebServer do
|
|
38
64
|
|
39
65
|
%w(json formencoded from_facebook).each do |mode|
|
40
66
|
[false, true].each do |parse_json|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
67
|
+
[:get, :post].each do |verb|
|
68
|
+
describe "#get_access_token (#{mode}, token_method=#{verb} parse_json=#{parse_json})" do
|
69
|
+
before do
|
70
|
+
@mode = mode
|
71
|
+
client.json=parse_json
|
72
|
+
client.token_method=verb
|
73
|
+
@access = subject.get_access_token('sushi')
|
74
|
+
end
|
47
75
|
|
48
|
-
|
49
|
-
|
50
|
-
|
76
|
+
it 'returns AccessToken with same Client' do
|
77
|
+
@access.client.should == client
|
78
|
+
end
|
51
79
|
|
52
|
-
|
53
|
-
|
54
|
-
|
80
|
+
it 'returns AccessToken with #token' do
|
81
|
+
@access.token.should == 'salmon'
|
82
|
+
end
|
55
83
|
|
56
|
-
|
57
|
-
|
58
|
-
|
84
|
+
it 'returns AccessToken with #refresh_token' do
|
85
|
+
@access.refresh_token.should == 'trout'
|
86
|
+
end
|
59
87
|
|
60
|
-
|
61
|
-
|
62
|
-
|
88
|
+
it 'returns AccessToken with #expires_in' do
|
89
|
+
@access.expires_in.should == 600
|
90
|
+
end
|
63
91
|
|
64
|
-
|
65
|
-
|
92
|
+
it 'returns AccessToken with #expires_at' do
|
93
|
+
@access.expires_at.should be_kind_of(Time)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'returns AccessToken with params accessible via []' do
|
97
|
+
@access['extra_param'].should == 'steve'
|
98
|
+
end
|
66
99
|
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
67
103
|
|
68
|
-
|
69
|
-
|
104
|
+
%w(json formencoded).each do |mode|
|
105
|
+
[false, true].each do |parse_json|
|
106
|
+
[:get].each do |verb|
|
107
|
+
describe "#refresh_access_token (#{mode}, token_method=#{verb} parse_json=#{parse_json})" do
|
108
|
+
before do
|
109
|
+
@mode = mode
|
110
|
+
client.json=parse_json
|
111
|
+
client.token_method=verb
|
112
|
+
@access = subject.refresh_access_token('trout')
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'returns AccessToken with same Client' do
|
116
|
+
@access.client.should == client
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'returns AccessToken with #token' do
|
120
|
+
@access.token.should == 'tuna'
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'returns AccessToken with #refresh_token' do
|
124
|
+
@access.refresh_token.should == 'trout'
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'returns AccessToken with #expires_in' do
|
128
|
+
@access.expires_in.should == 600
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'returns AccessToken with #expires_at' do
|
132
|
+
@access.expires_at.should be_kind_of(Time)
|
133
|
+
end
|
70
134
|
end
|
71
135
|
end
|
72
136
|
end
|
data/spec/spec_helper.rb
CHANGED
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.4.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Michael Bleigh
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-04-
|
13
|
+
date: 2011-04-20 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: faraday
|