oauthenticator 0.1.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dce07a7cd57f37e9644bbcfa1d9463af0bb3aaaf
4
- data.tar.gz: e6e8c3fba86aca134288dfe1bb2a1644a8a543e9
3
+ metadata.gz: 7006ebe89c4cd63acc4c40c404a81cd1d435c671
4
+ data.tar.gz: 54399c9d2d96645797f5bf0c179a46ac3aa0e47d
5
5
  SHA512:
6
- metadata.gz: 05375a2021880cc799ac90c4ce03c03bb0b39968841bb1922244e749da54199ba65a7893ae550e330ce1a139841ab2a77d236f26cb4c91d48f5824873a631095
7
- data.tar.gz: 8bfedffd4be1a4a3231f65421699eb1f8baee29587ced7d537a0606c85ae34d79445adfd157c7677cc6be854510abc7ca3803bca6d9b515a40822e51462114e9
6
+ metadata.gz: 85859997c9cba4592b097443e643528c6ccc479542eb0410e64a57611c6226a0988314d0b8adb3a17e3e0ae08b823d45a6bbeb75f0f15ca6c298c4b5581859ab
7
+ data.tar.gz: b1776d367f14799e6fcf5da5e09ed4c6c97cc932d86969c19c06bdcb99ac81827396c8d227fd445a64cbb8ae71f561a9ae4d7da9d65c6dfef67b3b788ce2c142
data/.simplecov ADDED
@@ -0,0 +1 @@
1
+ SimpleCov.start
data/README.md CHANGED
@@ -1,9 +1,89 @@
1
1
  # OAuthenticator
2
2
 
3
- OAuthenticator authenticates OAuth 1.0 signed requests, primarily as a middleware, and forms useful error
4
- messages when authentication fails.
3
+ OAuthenticator signs outgoing requests with OAuth 1.0.
5
4
 
6
- ## Config Methods module
5
+ OAuthenticator authenticates incoming OAuth 1.0 signed requests, primarily as a middleware, and forms useful
6
+ error messages when authentication fails.
7
+
8
+ Note: The canonical location of this README is on [RubyDoc](http://rubydoc.info/gems/oauthenticator/). When
9
+ viewed on [Github](https://github.com/notEthan/oauthenticator/), it may be inconsistent with the latest
10
+ released gem, and Yardoc links will not work.
11
+
12
+ ## Signing outgoing requests
13
+
14
+ ### Faraday
15
+
16
+ OAuthenticator provides Faraday middleware for easy signing of outgoing requests. This request middleware is
17
+ registered with faraday named `:oauthenticator_signer`.
18
+
19
+ The middleware should be in the stack immediately before the adapter. Any other middleware that modifies the
20
+ request between OAuthenticator signing it and the request actually being made may render the signature
21
+ invalid.
22
+
23
+ See the documentation for {OAuthenticator::FaradaySigner} for more detailed information.
24
+
25
+ An example:
26
+
27
+ ```ruby
28
+ require 'oauthenticator'
29
+
30
+ signing_options = {
31
+ :signature_method => 'HMAC-SHA1',
32
+ :consumer_key => 'a consumer',
33
+ :consumer_secret => 'a consumer secret',
34
+ :token => 'a token',
35
+ :token_secret => 'a token secret',
36
+ :realm => 'The Realm',
37
+ }
38
+
39
+ connection = Faraday.new('http://example.com/') do |faraday|
40
+ faraday.request :url_encoded
41
+ faraday.request :oauthenticator_signer, signing_options
42
+ faraday.adapter Faraday.default_adapter
43
+ end
44
+
45
+ connection.get '/path'
46
+ ```
47
+
48
+ Note that `:url_encoded` is only included to illustrate that other middleware should all go before
49
+ `:oauthenticator_signer`; the use of `:url_encoded` is not related to OAuthenticator.
50
+
51
+ ### Any other HTTP library
52
+
53
+ Generating an Authorization header to apply to an outgoing request is a relatively straightforward affair:
54
+
55
+ ```ruby
56
+ oauthenticator_signable_request = OAuthenticator::SignableRequest.new(
57
+ :request_method => my_request_method,
58
+ :uri => my_request_uri,
59
+ :media_type => my_request_media_type,
60
+ :body => my_request_body,
61
+ :signature_method => my_oauth_signature_method,
62
+ :consumer_key => my_oauth_consumer_key,
63
+ :consumer_secret => my_oauth_consumer_secret,
64
+ :token => my_oauth_token,
65
+ :token_secret => my_oauth_token_secret,
66
+ :realm => my_authorization_realm,
67
+ :hash_body? => my_body_hashing_requirement
68
+ )
69
+ my_http_request.headers['Authorization'] = oauthenticator_signable_request.authorization
70
+ ```
71
+
72
+ See the documentation for {OAuthenticator::SignableRequest} for more detailed information.
73
+
74
+ ### OAuth Request Body Hash
75
+
76
+ The [OAuth Request Body Hash](https://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html)
77
+ specification is supported. By default all signing of outgoing does include the body hash. This can be
78
+ disabled by setting the `:hash_body?` / `'hash_body?'` attribute to false when instantiating an
79
+ OAuthenticator::SignableRequest.
80
+
81
+ For info on when to include the body hash, see
82
+ [When to Include the Body Hash](https://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html#when_to_include).
83
+
84
+ ## Authenticating incoming requests
85
+
86
+ ### Config Methods module
7
87
 
8
88
  There are many ways (infinite, really) in which certain parts of the OAuth spec may be implemented. In order
9
89
  to flexibly accomodate the general case of OAuth authentication, OAuthenticator leaves certain parts of the
@@ -11,11 +91,11 @@ implementation up to the user. The user configures this by creating a module imp
11
91
  which will be passed to OAuthenticator.
12
92
 
13
93
  For more information on the details of the methods which must or may be implemented, please see the
14
- documentation for the module `OAuthenticator::ConfigMethods`, which defines stub methods for
94
+ documentation for the module {OAuthenticator::ConfigMethods}, which defines stub methods for
15
95
  each recognized method, with method documentation relating to your implementation.
16
96
 
17
97
  A simple, contrived example follows, which approximately resembles what you might implement. It is not useful
18
- on its own but will be used in following examples for usage of Middleware and SignedRequest.
98
+ on its own but will be used in following examples for usage of RackAuthenticator and SignedRequest.
19
99
 
20
100
  ```ruby
21
101
  require 'oauthenticator'
@@ -29,7 +109,7 @@ require 'oauthenticator'
29
109
  # - OAuthConsumer
30
110
  # - key
31
111
  # - secret
32
- # - OAuthAccessToken
112
+ # - OAuthToken
33
113
  # - token
34
114
  # - secret
35
115
  # - consumer_key
@@ -62,60 +142,63 @@ module AwesomeOAuthConfig
62
142
  OAuthConsumer.where(:key => consumer_key).first.try(:secret)
63
143
  end
64
144
 
65
- # access token secret, looked up by access token
66
- def access_token_secret
67
- OAuthAccessToken.where(:token => token).first.try(:secret)
145
+ # token secret, looked up by token
146
+ def token_secret
147
+ OAuthToken.where(:token => token).first.try(:secret)
68
148
  end
69
149
 
70
- # whether the access token belongs to the consumer
71
- def access_token_belongs_to_consumer?
72
- OAuthAccessToken.where(:token => token).first.try(:consumer_key) == consumer_key
150
+ # whether the token belongs to the consumer
151
+ def token_belongs_to_consumer?
152
+ OAuthToken.where(:token => token).first.try(:consumer_key) == consumer_key
73
153
  # alternately:
74
- # OAuthAccessToken.where(:token => token, :consumer_key => consumer_key).any?
154
+ # OAuthToken.where(:token => token, :consumer_key => consumer_key).any?
155
+ end
156
+
157
+ # whether oauth_body_hash_is_required (this method defaults to false and may be omitted)
158
+ def body_hash_required?
159
+ false
75
160
  end
76
161
  end
77
162
  ```
78
163
 
79
- You may also find it enlightening to peruse `test/oauthenticator_test.rb`. About the first thing it does is
80
- set up some very simple storage in memory, and define a module of config methods which are used through the
81
- tests.
164
+ You may also find it enlightening to peruse `test/test_config_methods.rb`, which sets up some very simple
165
+ storage in memory, and defines a module of config methods which are used through the tests.
82
166
 
83
- ## OAuthenticator::Middleware
167
+ ### OAuthenticator::RackAuthenticator
84
168
 
85
- The middleware is used by passing the above-mentioned module on the `:config_methods` key to initialize the
86
- middleware:
169
+ The RackAuthenticator middleware is used by passing the above-mentioned module on the `:config_methods` key to
170
+ initialize the middleware:
87
171
 
88
172
  ```ruby
89
173
  # config.ru
90
174
 
91
- use OAuthenticator::Middleware, :config_methods => AwesomeOAuthConfig
175
+ use OAuthenticator::RackAuthenticator, :config_methods => AwesomeOAuthConfig
92
176
  run proc { |env| [200, {'Content-Type' => 'text/plain'}, ['access granted!']] }
93
177
  ```
94
178
 
95
179
  The authentication can also be bypassed with a proc on the `:bypass` key; see the documentation for
96
- `OAuthenticator::Middleware` for the details of that.
180
+ {OAuthenticator::RackAuthenticator} for the details of that.
97
181
 
98
- ## OAuthenticator::SignedRequest
182
+ ### OAuthenticator::SignedRequest
99
183
 
100
- The OAuthenticator::SignedRequest class may be used independently of the middleware, though it must also be
101
- passed your module of config methods to include. It is used like:
184
+ The OAuthenticator::SignedRequest class may be used independently of the RackAuthenticator middleware, though
185
+ it must also be passed your module of config methods to include. It is used like:
102
186
 
103
187
  ```ruby
104
188
  OAuthenticator::SignedRequest.including_config(AwesomeOAuthConfig).new(request_attrs)
105
189
  ```
106
190
 
107
- See the documentation of OAuthenticator::SignedRequest for how the class is used, once it includes the methods
108
- it needs to function.
191
+ See the documentation of {OAuthenticator::SignedRequest} for how the class is used, once it includes the
192
+ methods it needs to function.
109
193
 
110
- # Other
194
+ ### OAuth Request Body Hash
111
195
 
112
- ## SimpleOAuth
196
+ The [OAuth Request Body Hash](https://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html)
197
+ specification is supported. Requests which include the oauth_body_hash parameter are authenticated according
198
+ to the spec.
113
199
 
114
- OAuthenticator uses [SimpleOAuth](https://github.com/laserlemon/simple_oauth) underneath. There is a
115
- fork with some improvements that have not yet made it into the main SimpleOAuth repo, and it is recommended
116
- to use these for more robust and safe parsing of the Authorization header. This is not published in rubygems,
117
- but if you use Bundler, you can use this by using the following line in your `Gemfile`;
200
+ Requests which may include the oauth_body_hash parameter but do not are accepted or rejected based on the
201
+ config method `#body_hash_required?` - if the implementation indicates that oauth_body_hash is required, then
202
+ the request is rejected as inauthentic; if it is not required then the request is allowed (assuming all other
203
+ aspects of the OAuth signature are authentic.)
118
204
 
119
- ```ruby
120
- gem 'simple_oauth', :git => 'https://github.com/notEthan/simple_oauth.git', :tag => 'ethan-v0.2.0.2'
121
- ```
data/Rakefile.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'rake/testtask'
2
+ Rake::TestTask.new do |t|
3
+ t.name = 'test'
4
+ t.test_files = FileList['test/**/*_test.rb']
5
+ t.verbose = true
6
+ end
7
+ task 'default' => 'test'
8
+
9
+ require 'yard'
10
+ YARD::Rake::YardocTask.new do |t|
11
+ end
@@ -1,6 +1,5 @@
1
- require "oauthenticator/version"
2
-
3
- module OAuthenticator
4
- autoload :Middleware, 'oauthenticator/middleware'
5
- autoload :SignedRequest, 'oauthenticator/signed_request'
6
- end
1
+ require 'oauthenticator/version'
2
+ require 'oauthenticator/signed_request'
3
+ require 'oauthenticator/signable_request'
4
+ require 'oauthenticator/rack_authenticator'
5
+ require 'oauthenticator/faraday_signer'
@@ -1,14 +1,19 @@
1
1
  module OAuthenticator
2
- # This module contains stubs, or in some cases default values, for implementations of particulars of the OAuth protocol. Applications must implement some of these, and are likely to want to override the default values of others. certain methods will need to use methods of the {OAuthenticator::SignedRequest} class.
2
+ # This module contains stubs, or in some cases default values, for implementations of particulars of the
3
+ # OAuth protocol. Applications must implement some of these, and are likely to want to override the default
4
+ # values of others. certain methods will need to use methods of the {OAuthenticator::SignedRequest} class.
3
5
  #
4
- # the methods your implementation will need to be used are primarily those from the parsed OAuth Authorization header. these are methods your implementation WILL need to use to implement the required functionality:
6
+ # the methods your implementation will need to be used are primarily those from the parsed OAuth
7
+ # Authorization header. these are methods your implementation WILL need to use to implement the required
8
+ # functionality:
5
9
  #
6
10
  # - `#consumer_key`
7
11
  # - `#token`
8
12
  # - `#nonce`
9
13
  # - `#timestamp`
10
14
  #
11
- # the following are the other parts of the Authorization, but your implementation will probably NOT need to use these (OAuthenticator does everything that is needed to validate these parts):
15
+ # the following are the other parts of the Authorization, but your implementation will probably NOT need to
16
+ # use these (OAuthenticator does everything that is needed to validate these parts):
12
17
  #
13
18
  # - `#version`
14
19
  # - `#signature_method`
@@ -16,9 +21,12 @@ module OAuthenticator
16
21
  module ConfigMethods
17
22
  # the number of seconds (integer) in both the past and future for which the request is considered valid.
18
23
  #
19
- # if it is desired to have a different period considered valid in the past than in the future, then the methods {#timestamp_valid_past} and {#timestamp_valid_future} may be implemented instead, and this method may remain unimplemented.
24
+ # if it is desired to have a different period considered valid in the past than in the future, then the
25
+ # methods {#timestamp_valid_past} and {#timestamp_valid_future} may be implemented instead, and this
26
+ # method may remain unimplemented.
20
27
  #
21
- # see the documentation for {#timestamp_valid_past} and {#timestamp_valid_future} for other considerations of the valid period.
28
+ # see the documentation for {#timestamp_valid_past} and {#timestamp_valid_future} for other considerations
29
+ # of the valid period.
22
30
  #
23
31
  # @return [Integer] period in seconds
24
32
  def timestamp_valid_period
@@ -27,9 +35,11 @@ module OAuthenticator
27
35
 
28
36
  # the number of seconds (integer) in the past for which the request is considered valid.
29
37
  #
30
- # if the timestamp is more than this number of seconds less than the current clock time, then the request is considered invalid and the response is an error.
38
+ # if the timestamp is more than this number of seconds less than the current clock time, then the request
39
+ # is considered invalid and the response is an error.
31
40
  #
32
- # this should be large enough to allow for clock skew between your application's server and the requester's clock.
41
+ # this should be large enough to allow for clock skew between your application's server and the
42
+ # requester's clock.
33
43
  #
34
44
  # nonces older than Time.now - timestamp_valid_past may be discarded.
35
45
  #
@@ -42,9 +52,11 @@ module OAuthenticator
42
52
 
43
53
  # the number of seconds (integer) in the future for which the request is considered valid.
44
54
  #
45
- # if the timestamp is more than this number of seconds greater than the current clock time, then the request is considered invalid and the response is an error.
55
+ # if the timestamp is more than this number of seconds greater than the current clock time, then the
56
+ # request is considered invalid and the response is an error.
46
57
  #
47
- # this should be large enough to allow for clock skew between your application's server and the requester's clock.
58
+ # this should be large enough to allow for clock skew between your application's server and the
59
+ # requester's clock.
48
60
  #
49
61
  # this method may remain unimplemented if {#timestamp_valid_period} is implemented.
50
62
  #
@@ -53,48 +65,77 @@ module OAuthenticator
53
65
  timestamp_valid_period
54
66
  end
55
67
 
56
- # the signature methods which the application will accept. this MUST be a subset of the signature methods defined in the OAuth 1.0 protocol: `%w(HMAC-SHA1 RSA-SHA1 PLAINTEXT)`. the default value for this is all allowed signature methods, and may remain unimplemented if you wish to allow all defined signature methods.
68
+ # the signature methods which the application will accept. this MUST be a subset of the signature methods
69
+ # defined in the OAuth 1.0 protocol: `%w(HMAC-SHA1 RSA-SHA1 PLAINTEXT)`. the default value for this is all
70
+ # allowed signature methods, and may remain unimplemented if you wish to allow all defined signature
71
+ # methods.
57
72
  #
58
73
  # @return [Array<String>]
59
74
  def allowed_signature_methods
60
- OAuthenticator::SignedRequest::VALID_SIGNATURE_METHODS
75
+ SignableRequest::SIGNATURE_METHODS.keys
61
76
  end
62
77
 
63
- # this should look up the consumer secret in your application's storage corresponding to the request's consumer key, which is available via the `#consumer_key` method. see the README for an example implementation.
78
+ # this should look up the consumer secret in your application's storage corresponding to the request's
79
+ # consumer key, which is available via the `#consumer_key` method. see the README for an example
80
+ # implementation.
64
81
  #
65
82
  # @return [String] the consumer secret for the request's consumer key
66
83
  def consumer_secret
67
84
  config_method_not_implemented
68
85
  end
69
86
 
70
- # this should look up the access token secret in your application's storage corresponding to the request's access token, which is available via the `#token` method. see the README for an example implementation.
87
+ # this should look up the token secret in your application's storage corresponding to the request's
88
+ # token, which is available via the `#token` method. see the README for an example implementation.
71
89
  #
72
- # @return [String] the access token secret for the request's access token
73
- def access_token_secret
90
+ # @return [String] the token secret for the request's token
91
+ def token_secret
74
92
  config_method_not_implemented
75
93
  end
76
94
 
77
- # whether the nonce, available via the `#nonce` method, has already been used. you may wish to use this in conjunction with the timestamp (`#timestamp`), per the OAuth 1.0 spec.
95
+ # whether the nonce, available via the `#nonce` method, has already been used. you may wish to use this in
96
+ # conjunction with the timestamp (`#timestamp`), per the OAuth 1.0 spec.
78
97
  #
79
- # it's worth noting that if this ever returns true, it may indicate a replay attack under way against your application. the replay attack will fail due to OAuth, but you may wish to log the event.
98
+ # it's worth noting that if this ever returns true, it may indicate a replay attack under way against your
99
+ # application. the replay attack will fail due to OAuth, but you may wish to log the event.
80
100
  #
81
101
  # @return [Boolean] whether the request's nonce has already been used.
82
102
  def nonce_used?
83
103
  config_method_not_implemented
84
104
  end
85
105
 
86
- # cause the nonce, available via the `#nonce` method, to be marked as used. you may wish to use this in conjunction with the timestamp (`#timestamp`).
106
+ # cause the nonce, available via the `#nonce` method, to be marked as used. you may wish to use this in
107
+ # conjunction with the timestamp (`#timestamp`).
87
108
  #
88
109
  # @return [Void] (return value is ignored / unused)
89
110
  def use_nonce!
90
111
  config_method_not_implemented
91
112
  end
92
113
 
93
- # whether the access token indicated by the request (via `#token`) belongs to the consumer indicated by the request (via `#consumer_key`).
114
+ # whether the token indicated by the request (via `#token`) belongs to the consumer indicated by
115
+ # the request (via `#consumer_key`).
94
116
  #
95
- # @return [Boolean] whether the request's access token belongs to the request's consumer
96
- def access_token_belongs_to_consumer?
117
+ # this method may simply return true if the implementation does not care to restrict tokens by
118
+ # consumer.
119
+ #
120
+ # @return [Boolean] whether the request's token belongs to the request's consumer
121
+ def token_belongs_to_consumer?
97
122
  config_method_not_implemented
98
123
  end
124
+
125
+ # whether the request will be considered valid if it is missing the oauth_body_hash parameter, when that
126
+ # parameter is allowed. if you require requests to include the oauth_body_hash parameter, return true
127
+ # here.
128
+ #
129
+ # the default for this method is false, since the oauth body hash is not widely implemented.
130
+ #
131
+ # this only applies to requests which are NOT form encoded - requests which are form-encoded must never
132
+ # have the oauth_body_hash parameter regardless of this setting and will be rejected as inauthentic, per
133
+ # the OAuth Request Body Hash spec. this also does not apply to requests with a signature method which
134
+ # does not have a corresponding body hash method - i.e., the PLAINTEXT signature method.
135
+ #
136
+ # @return [Boolean] whether body hash is required
137
+ def body_hash_required?
138
+ false
139
+ end
99
140
  end
100
141
  end
@@ -0,0 +1,66 @@
1
+ require 'faraday'
2
+
3
+ if Faraday.respond_to?(:register_middleware)
4
+ Faraday.register_middleware(:request, :oauthenticator_signer => proc { OAuthenticator::FaradaySigner })
5
+ end
6
+ if Faraday::Request.respond_to?(:register_middleware)
7
+ Faraday::Request.register_middleware(:oauthenticator_signer => proc { OAuthenticator::FaradaySigner })
8
+ end
9
+
10
+ module OAuthenticator
11
+ # OAuthenticator Faraday middleware to sign outgoing requests.
12
+ #
13
+ # The middleware should be in the stack immediately before the adapter. Any other middleware that modifies
14
+ # the request between OAuthenticator signing it and the request actually being made may render the signature
15
+ # invalid.
16
+ #
17
+ # This request middleware is registered as `:oauthenticator_signer`. It should be used like
18
+ #
19
+ # connection = Faraday.new('http://example.com/') do |faraday|
20
+ # faraday.request :url_encoded
21
+ # faraday.request :oauthenticator_signer, signing_options
22
+ # faraday.adapter Faraday.default_adapter
23
+ # end
24
+ #
25
+ # Note that `:url_encoded` is only included to illustrate that other middleware should all go before
26
+ # `:oauthenticator_signer`; the use of `:url_encoded` is not related to OAuthenticator.
27
+ #
28
+ # See {#initialize} for details of what the `signing_options` hash should include.
29
+ class FaradaySigner
30
+ # options are passed to {OAuthenticator::SignableRequest}.
31
+ #
32
+ # attributes of the request are added by the middleware, so you should not provide those as optiosn
33
+ # (it would not make sense to do so on the connection level).
34
+ #
35
+ # These are the options you should or may provide (see {OAuthenticator::SignableRequest} for details of
36
+ # what options are required, what options have default or generated values, and what may be omitted):
37
+ #
38
+ # - signature_method
39
+ # - consumer_key
40
+ # - consumer_secret
41
+ # - token
42
+ # - token_secret
43
+ # - version
44
+ # - realm
45
+ # - hash_body?
46
+ def initialize(app, options)
47
+ @app = app
48
+ @options = options
49
+ end
50
+
51
+ # do the thing
52
+ def call(request_env)
53
+ request_attributes = {
54
+ :request_method => request_env[:method],
55
+ :uri => request_env[:url],
56
+ :media_type => request_env[:request_headers]['Content-Type'],
57
+ :body => request_env[:body]
58
+ }
59
+ oauthenticator_signable_request = OAuthenticator::SignableRequest.new(@options.merge(request_attributes))
60
+ authorization = oauthenticator_signable_request.authorization
61
+ signed_request_headers = request_env[:request_headers].merge('Authorization' => authorization)
62
+ signed_request_env = request_env.merge(:request_headers => signed_request_headers)
63
+ @app.call(signed_request_env)
64
+ end
65
+ end
66
+ end