oauth2 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,22 +1,20 @@
1
- 0.0.10 - June 19, 2010
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.9 - June 18, 2010
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, 2010
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, 2010
16
- ----------------------
17
- * Updating Faraday dependency for improved SSL support (via technoweenie)
18
-
19
- 0.0.6 - April 25, 2010
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
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)
@@ -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'
@@ -27,7 +27,9 @@ module OAuth2
27
27
  end
28
28
 
29
29
  def request(verb, path, params={}, headers={})
30
- params = params.merge token_param => @token
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
@@ -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
@@ -51,7 +51,7 @@ module OAuth2
51
51
  include ResponseObject
52
52
 
53
53
  def initialize(response)
54
- super(response.body)
54
+ super(response.body.to_s)
55
55
  self.response = response
56
56
  end
57
57
  end
@@ -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(:get, @client.access_token_url, access_token_params(code, options))
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
@@ -1,3 +1,3 @@
1
1
  module OAuth2
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -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') {|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']]}
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
- it "makes GET requests with access token" do
34
- subject.send(:get, 'client').should == 'get'
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 delete).each do |http_method|
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
@@ -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
- describe "#get_access_token (#{mode}, parse_json=#{parse_json})" do
42
- before do
43
- @mode = mode
44
- client.json=parse_json
45
- @access = subject.get_access_token('sushi')
46
- end
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
- it 'returns AccessToken with same Client' do
49
- @access.client.should == client
50
- end
76
+ it 'returns AccessToken with same Client' do
77
+ @access.client.should == client
78
+ end
51
79
 
52
- it 'returns AccessToken with #token' do
53
- @access.token.should == 'salmon'
54
- end
80
+ it 'returns AccessToken with #token' do
81
+ @access.token.should == 'salmon'
82
+ end
55
83
 
56
- it 'returns AccessToken with #refresh_token' do
57
- @access.refresh_token.should == 'trout'
58
- end
84
+ it 'returns AccessToken with #refresh_token' do
85
+ @access.refresh_token.should == 'trout'
86
+ end
59
87
 
60
- it 'returns AccessToken with #expires_in' do
61
- @access.expires_in.should == 600
62
- end
88
+ it 'returns AccessToken with #expires_in' do
89
+ @access.expires_in.should == 600
90
+ end
63
91
 
64
- it 'returns AccessToken with #expires_at' do
65
- @access.expires_at.should be_kind_of(Time)
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
- it 'returns AccessToken with params accessible via []' do
69
- @access['extra_param'].should == 'steve'
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
@@ -1,3 +1,7 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+ Bundler.setup
4
+
1
5
  require 'simplecov'
2
6
  SimpleCov.start
3
7
  require 'oauth2'
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: oauth2
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.3.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-08 00:00:00 Z
13
+ date: 2011-04-20 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: faraday