Skip to content

Commit f7e2b02

Browse files
authored
Add Excon support (#218)
* Add Excon support * Fix spacing * Rubocops * Fix API compatibility * Improve IO body signing
1 parent 0b73681 commit f7e2b02

File tree

10 files changed

+483
-58
lines changed

10 files changed

+483
-58
lines changed

.rubocop_todo.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@ Lint/Void:
3838
Metrics/CyclomaticComplexity:
3939
Max: 16
4040

41-
# Offense count: 11
41+
# Offense count: 12
4242
Naming/AccessorMethodName:
4343
Exclude:
4444
- 'lib/api_auth/railtie.rb'
4545
- 'lib/api_auth/request_drivers/action_controller.rb'
4646
- 'lib/api_auth/request_drivers/curb.rb'
47+
- 'lib/api_auth/request_drivers/excon.rb'
4748
- 'lib/api_auth/request_drivers/faraday.rb'
4849
- 'lib/api_auth/request_drivers/faraday_env.rb'
4950
- 'lib/api_auth/request_drivers/grape_request.rb'

CHANGELOG.md

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
# 3.0.0 (Unreleased)
22

33
## Breaking Changes
4+
45
- **Drop support for Ruby < 3.2** - Now requires Ruby 3.2, 3.3, or 3.4+
56
- **Drop support for Rails 6.x** - Now requires Rails 7.2+ or 8.0+
67
- **Remove deprecated Ruby 2.x compatibility code**
78

9+
## New Features
10+
11+
- Add Excon HTTP client support with middleware (based on contribution by @stiak in PR #154)
12+
813
## Improvements
14+
915
- Update minimum Ruby version to 3.2
1016
- Update minimum Rails version to 7.2
1117
- Support Rails 8.0
1218
- Remove explicit drb dependency (now provided by Rails 7.2+)
1319
- Update development dependencies to latest versions:
20+
- Excon ~> 0.100
1421
- Faraday ~> 2.0
1522
- HTTP ~> 5.0
1623
- HTTPi ~> 4.0
@@ -19,6 +26,7 @@
1926
- Rest-Client ~> 2.1
2027

2128
# 2.6.0 (2025-01-18)
29+
2230
- Add Faraday middleware support (#1322051 Frédéric Mangano)
2331
- Add MD5 compatibility option in authentic? method (#a618e15 Samir ALI CHERIF)
2432
- Add support for Ruby 3.1 and Rails 7.0 (#552cab0 fwininger)
@@ -30,13 +38,15 @@
3038
- Add drb gem dependency (2.0.4-2.0.5) for Ruby 3.4+ compatibility while avoiding Ruby 2.6 conflicts
3139

3240
# 2.5.1 (2021-11-26)
41+
3342
- Add spec coverage for all content hashes (#202 fwininger)
3443
- Require MFA for Rubygems (#203 fwininger)
3544
- Integration with GitHub Actions
3645
- Fix look up of `X-AUTHORIZATION-CONTENT-SHA256` header
3746
- Adding license information to the gemspec
3847

3948
# 2.5.0 (2021-05-11)
49+
4050
- Add support for Ruby 3.0 (#194 fwininger)
4151
- Add support for Rails 6.1 (#194 fwininger)
4252
- Drop support for Ruby 2.4 (#193 fwininger)
@@ -45,23 +55,28 @@
4555
- Fix Faraday warning: `WARNING: Faraday::Request#method is deprecated` (#191 fwininger)
4656

4757
# 2.4.1 (2020-06-23)
58+
4859
- Fix inadvertent ActiveSupport dependency (#189 taylorthurlow)
4960

5061
# 2.4.0 (2020-05-05)
62+
5163
- Improved support for Rails 6.0 (#179 taylorthurlow, #177 fwininger)
5264
- Added Ruby 2.6.0 support (#174 fwininger)
5365
- README updates (#186 iranthau)
5466

5567
# 2.3.1 (2018-11-06)
68+
5669
- Fixed a regression in the http.rb driver (#173 tycooon)
5770

5871
# 2.3.0 (2018-10-23)
72+
5973
- Added support for Grape API (#169 phuongnd08 & dunghuynh)
6074
- Added option for specifying customer headers to sign via new `headers_to_sign`
6175
argument (#170 fakenine)
6276
- Fix tests and drop support for Ruby < 2.3 (#171 fwininger)
6377

6478
# 2.2.0 (2018-03-12)
79+
6580
- Drop support ruby 1.x, rails 2.x, rails 3.x (#141 fwininger)
6681
- Add http.rb request driver (#164 tycooon)
6782
- Fix POST and PUT requests in RestClient (#151 fwininger)
@@ -73,107 +88,120 @@
7388
- Updates to the README (zfletch)
7489

7590
# 2.1.0 (2016-12-22)
91+
7692
- Fixed a NoMethodError that might occur when using the NetHttp Driver (#130 grahamkenville)
7793
- More securely compare signatures in a way that prevents timing attacks (#56 leishman, #133 will0)
7894
- Remove support for MD2 and MD4 hashing algorithms since they are insecure (#134 will0)
7995
- Disallow requests that are too far in the future to limit the time available for a brute force signature guess (#119 fwininger)
8096

8197
# 2.0.1 (2016-07-25)
98+
8299
- Support of `api_auth_options` in ActiveResource integration (#102 fwininger)
83100
- Replace use of `#blank?` with `#nil?` to not depend on ActiveSupport (#114 packrat386)
84101
- Fix Auth header matching to not match invalid SHA algorithms (#115 packrat386)
85102
- Replace `alias_method_chain` with `alias_method` in the railtie since
86103
alias_method_chain is deprecated in Rails 5 (#118 mlarraz)
87104

88105
# 2.0.0 (2016-05-11)
106+
89107
- IMPORTANT: 2.0.0 is backwards incompatible with the default settings of v1.x
90108
v2.0.0 always includes the http method in the canonical string.
91109
You can use the upgrade strategy in v1.4.x and above to migrate to v2.0.0
92110
without any down time. Please see the 1.4.0 release nodes for more info
93111
- Added support for other digest algorithms like SHA-256 (#98 fwininger)
94112

95113
# 1.5.0 (2016-01-21)
114+
96115
- Added a sign_with_http_method configuration option to the ActiveResource
97116
rails tie to correspond to passing the `:with_http_method => true` into
98117
`ApiAuth.sign!`
99118

100119
# 1.4.1 (2016-01-04)
120+
101121
- Fixed an issue where getters wouldn't immediately have the correct value after
102122
setting a date or content md5 in some of the request drivers (#91)
103123

104124
# 1.4.0 (2015-12-16)
105125

106126
## IMPORTANT SECURITY FIX (with backwards compatible fallback)
107127

108-
This version introduces a security fix. In previous versions, the canonical
109-
string does not include the http method used to make the request, this means
110-
two requests that would otherwise be identical (such as a GET and DELETE)
111-
would have the same signature allowing for a MITM to swap one method for
112-
another.
128+
This version introduces a security fix. In previous versions, the canonical
129+
string does not include the http method used to make the request, this means
130+
two requests that would otherwise be identical (such as a GET and DELETE)
131+
would have the same signature allowing for a MITM to swap one method for
132+
another.
113133

114-
In ApiAuth v1.4 `ApiAuth.authentic?` will allow for requests signed using either
115-
the canonical string WITH the http method, or WITHOUT it. `ApiAuth.sign!` will,
116-
by default, still sign the request using the canonical string without the
117-
method. However, passing in the `:with_http_method => true` option into
118-
`ApiAuth.sign?` will cause the request to use the http method as part of the
119-
canonical string.
134+
In ApiAuth v1.4 `ApiAuth.authentic?` will allow for requests signed using either
135+
the canonical string WITH the http method, or WITHOUT it. `ApiAuth.sign!` will,
136+
by default, still sign the request using the canonical string without the
137+
method. However, passing in the `:with_http_method => true` option into
138+
`ApiAuth.sign?` will cause the request to use the http method as part of the
139+
canonical string.
120140

121-
Example:
141+
Example:
122142

123-
```ruby
124-
ApiAuth.sign!(request, access_id, secret_key, {:with_http_method => true})
125-
```
143+
```ruby
144+
ApiAuth.sign!(request, access_id, secret_key, {:with_http_method => true})
145+
```
126146

127-
This allows for an upgrade strategy that would look like the following.
147+
This allows for an upgrade strategy that would look like the following.
128148

129-
1. Update server side code to use ApiAuth v1.4
130-
2. Update client side code to use ApiAuth v1.4
131-
3. Update all client side code to sign with http method
132-
4. Update server side code to ApiAuth v2.0 (removes the ability to authenticate without the http method)
133-
5. Update all client side code to ApiAuth v2.0 (forces all signatures to contain the http method)
149+
1. Update server side code to use ApiAuth v1.4
150+
2. Update client side code to use ApiAuth v1.4
151+
3. Update all client side code to sign with http method
152+
4. Update server side code to ApiAuth v2.0 (removes the ability to authenticate without the http method)
153+
5. Update all client side code to ApiAuth v2.0 (forces all signatures to contain the http method)
134154

135155
## Additional changes
136156

137-
- Performance enhancement: reduce allocation of Headers object (#81 pd)
138-
- Performance enhancement: avoid reallocating static Regexps (#82 pd)
157+
- Performance enhancement: reduce allocation of Headers object (#81 pd)
158+
- Performance enhancement: avoid reallocating static Regexps (#82 pd)
139159

140160
# 1.3.2 (2015-08-28)
161+
141162
- Fixed a bug where some client adapters didn't treat an empty path as
142163
"/" in the canonical string (#75 managr)
143164

144165
# 1.3.1 (2015-03-13)
166+
145167
- Fixed a bug where Faraday requests with no parameters were not signed
146168
correctly (#65 nathanhoel)
147169

148170
# 1.3.0 (2015-03-12)
171+
149172
- Add a Faraday Request Driver (#64 nathanhoel)
150173

151174
# 1.2.6 (2014-10-01)
175+
152176
- Fix a bug in the ActionController request driver where calculated_md5 was
153177
incorrect in certain scenarios. (#53 karl-petter)
154178

155179
# 1.2.5 (2014-09-09)
180+
156181
- Fix a bug where ApiAuth.authentic? would cause an ArgumentError when given a
157182
request with an invalid date in the date header. It will now return false
158183
instead. (#51 Nakort)
159184

160185
# 1.2.4 (2014-08-27)
186+
161187
- Fix a bug in the Net::HTTP request driver where the md5 isn't calculated
162188
correctly when the content of the request is set with the `.body_stream`
163189
method. (#49 adamcrown)
164190

165191
# 1.2.3 (2014-08-01)
192+
166193
- Update action controller request driver to fix a bug with OLD versions of
167194
Rails using CGI
168195

169196
# 1.2.2 (2014-07-08)
197+
170198
- Fix Rest Client driver to account for the generated date when signing (cjeeky)
171199

172200
# 1.2.1 (2014-07-03)
173201

174202
- Fix Rest Client driver to account for the generated md5 when signing
175203
(#45 cjeeky)
176-
- Support for testing against Rails 4.1 (#42 awendt)
204+
- Support for testing against Rails 4.1 (#42 awendt)
177205
- Support all requests inheriting from Rack::Request (#43 mcls)
178206

179207
# 1.2.0 (2014-05-16)

README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ added as a request driver.
9797
* **Faraday** - Modular HTTP client library (with middleware support)
9898
* **HTTPI** - Common interface for Ruby HTTP clients
9999
* **HTTP** (http.rb) - Fast Ruby HTTP client with a chainable API
100+
* **Excon** - Pure Ruby HTTP client for API interactions (with middleware support)
100101
* **Grape** - REST-like API framework for Ruby (via Rack)
101102
* **Rack::Request** - Generic Rack request objects
102103

@@ -277,6 +278,52 @@ signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
277278

278279
The order of middlewares is important. You should make sure api_auth is added after any middleware that modifies the request body or content-type header.
279280

281+
#### Excon
282+
283+
Excon can be used with ApiAuth in two ways - with middleware or by manually signing requests.
284+
285+
Using Excon middleware (recommended):
286+
287+
```ruby
288+
require 'excon'
289+
require 'excon/api_auth' # or require 'api_auth/middleware/excon'
290+
291+
# Configure Excon with ApiAuth credentials
292+
Excon.defaults[:api_auth_access_id] = @access_id
293+
Excon.defaults[:api_auth_secret_key] = @secret_key
294+
Excon.defaults[:middlewares] << ApiAuth::Middleware::Excon
295+
296+
# All requests will be automatically signed
297+
connection = Excon.new('https://api.example.com')
298+
response = connection.post(
299+
path: '/resource',
300+
headers: { 'Content-Type' => 'application/json' },
301+
body: '{"key": "value"}'
302+
)
303+
```
304+
305+
Manual signing (when you need more control):
306+
307+
```ruby
308+
require 'excon'
309+
require 'api_auth'
310+
311+
connection = Excon.new('https://api.example.com')
312+
request_params = {
313+
method: :post,
314+
path: '/resource',
315+
headers: { 'Content-Type' => 'application/json' },
316+
body: '{"key": "value"}'
317+
}
318+
319+
# Create a wrapper for signing
320+
request = ApiAuth::Middleware::ExconRequestWrapper.new(request_params, '')
321+
ApiAuth.sign!(request, @access_id, @secret_key)
322+
323+
# Execute the request with signed headers
324+
response = connection.request(request_params)
325+
```
326+
280327
### ActiveResource Clients
281328
282329
ApiAuth can transparently protect your ActiveResource communications with a

api_auth.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
2222
s.add_development_dependency 'amatch'
2323
s.add_development_dependency 'appraisal'
2424
s.add_development_dependency 'curb', '~> 1.0'
25+
s.add_development_dependency 'excon', '~> 0.100'
2526
s.add_development_dependency 'faraday', '~> 2.0'
2627
s.add_development_dependency 'grape', '~> 2.0'
2728
s.add_development_dependency 'http', '~> 5.0'

lib/api_auth.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
require 'api_auth/request_drivers/faraday'
1717
require 'api_auth/request_drivers/faraday_env'
1818
require 'api_auth/request_drivers/http'
19+
require 'api_auth/request_drivers/excon'
1920

2021
require 'api_auth/headers'
2122
require 'api_auth/base'

0 commit comments

Comments
 (0)