simple_oauth 1.0.1 → 1.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48873a9ac8c602b3dc3d305445c3e44524eee89e876e53222928fdf17902b22b
4
- data.tar.gz: 166bcb795c1b953adf9c9dfaea2d6c69d8f00ba1bbc2953706ea9e7cca3fa87a
3
+ metadata.gz: c80dd2b6480173dc8278a8b927f6b25929bddb430a72e152abbd29c8cae4c4da
4
+ data.tar.gz: ac11a3c3e75fb0d1eb7964d25e5f53b002b334e27273e9e6428b5a6b9226481f
5
5
  SHA512:
6
- metadata.gz: 377164f7799c7e6f330b2ac80fcf144380c270e6a65c40b6fb272e1fca6fd9a52515161f7237a96a3fefd9d6efd03000fd675141fc52f6330fe4daeabd6ee854
7
- data.tar.gz: 6585353e86aa82b24cb3d08dc41e5ec12162b5866b2e35b25832a1603114cb1472f6323ab503be5ff47d69eedddff63ff136e1726e7e0672b677aaa681a22a52
6
+ metadata.gz: 7749f45d33c7f55a4186b9a744ea4119fe9d0ce1211ee26c89789887cad66f0a478dfddf4146e585044078adbaae82f548620794b71961a9da832cd8888ee5d3
7
+ data.tar.gz: bc81ed8ad865e68843a764e75fc04b06afa4cd441fb4b24a22406c8512ec844dfd01a975dde11ea88bf96f1d4679a96b23fbe63463db5735803193781d72c39c
data/.rubocop.yml CHANGED
@@ -7,8 +7,14 @@ plugins:
7
7
  - rubocop-performance
8
8
  - rubocop-rake
9
9
 
10
+ inherit_mode:
11
+ merge:
12
+ - Include
13
+
10
14
  AllCops:
11
15
  NewCops: enable
16
+ Include:
17
+ - .duel.rb
12
18
  TargetRubyVersion: 3.2
13
19
 
14
20
  Layout/ArgumentAlignment:
data/CHANGELOG.md CHANGED
@@ -1,103 +1,128 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/2.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.2] - 2026-11-14
9
+
10
+ ### Security
11
+
12
+ - `Header#valid?` no longer verifies with whatever signature method the received header claims. A server verifying RSA signatures with a client's public key, as the README showed, accepted forged requests signed with that public key as an HMAC-SHA1 or PLAINTEXT secret. Reported by Rathnadeepan (HotWater, [@HotWater-015](https://github.com/HotWater-015))
13
+ - Given a `signature_method`, the header must claim that method
14
+ - Without one, the header must claim an RSA method for an RSA key and an HMAC method for any other secret
15
+ - PLAINTEXT signatures verify only when the caller names `PLAINTEXT`
16
+
17
+ ### Deprecated
18
+
19
+ - Calling `Header#valid?` without a `signature_method`, which a later major version will require
20
+
1
21
  ## [1.0.1] - 2026-09-30
2
22
 
3
23
  ### Fixed
4
24
 
5
- * Ship RBS signatures that leave core and the standard library alone: the gem no longer redeclares `MatchData#[]`, `OpenSSL.secure_compare`, `OpenSSL::PKey::PKey#sign` and `#verify`, or `StringScanner`, so code that loads its signatures through `rbs collection` no longer fails `rbs validate` with a duplicated `MatchData#[]` definition
6
- * Add `sig/manifest.yaml`, naming the standard libraries the signatures refer to (`json`, `openssl`, `securerandom`, `strscan`, and `uri`), so `rbs collection` loads them for code that depends on the gem
25
+ - Ship RBS signatures that leave core and the standard library alone: the gem no longer redeclares `MatchData#[]`, `OpenSSL.secure_compare`, `OpenSSL::PKey::PKey#sign` and `#verify`, or `StringScanner`, so code that loads its signatures through `rbs collection` no longer fails `rbs validate` with a duplicated `MatchData#[]` definition
26
+ - Add `sig/manifest.yaml`, naming the standard libraries the signatures refer to (`json`, `openssl`, `securerandom`, `strscan`, and `uri`), so `rbs collection` loads them for code that depends on the gem
7
27
 
8
28
  ## [1.0.0] - 2026-09-12
9
29
 
10
- ### Changed
30
+ ### Added
11
31
 
12
- * Every file declares `frozen_string_literal: true`, and the explicit `freeze` calls on string literals it makes redundant are gone
13
- * Drop the `base64` runtime dependency: `pack("m0")` and `unpack1("m")` encode and decode Base64 in core Ruby, so the gem now has no runtime dependencies at all
14
- * Drop the `cgi` runtime dependency: query strings and form bodies are read with `URI.decode_www_form`, which Ruby ships in every supported version
15
- * **Breaking**: raise `ParseError` when an Authorization header, form body, or query string repeats an OAuth protocol parameter, which RFC 5849 Section 3.2 does not allow; `Header.parse` previously kept the last value and `Header.parse_form_body` the first, so the two disagreed about which one a request carried
16
- * **Breaking**: rename `Signature.methods` to `Signature.registered_methods`, so that `Signature.methods` is the module's own method list again
17
- * **Breaking**: define `VERSION` in `SimpleOAuth`, the module the rest of the library uses, rather than in a second `SimpleOauth` module; `SimpleOauth::VERSION` is gone and `SimpleOAuth::VERSION` now resolves
18
- * **Breaking**: `Signature.rsa?` raises `ArgumentError` for a signature method that is not registered, as `Signature.digest`, `sign`, and `verify` already do, rather than answering false
19
- * `SimpleOAuth::Error` is the base of every error the library raises, so `rescue SimpleOAuth::Error` catches `ParseError`, `InvalidOptionsError`, and `OAuth2::Error` alike
32
+ - OAuth 2.0 request builders and response parsers in `SimpleOAuth::OAuth2`, which make no HTTP requests:
33
+ - `Client#authorization_url` for the authorization code flow, which names `pkce` so that a client either sends a challenge or says `pkce: nil`, as OAuth 2.1 asks every client for one; a `state` is optional alongside a challenge, and required without one
34
+ - `Client#authorization_code_request`, `#refresh_token_request`, and `#client_credentials_request` for the token endpoint
35
+ - `Client#revocation_request` for the revocation endpoint (RFC 7009)
36
+ - `client_secret_basic` and `client_secret_post` authentication for confidential clients, and public clients without a secret
37
+ - `PKCE` verifiers with `S256` and `plain` challenges (RFC 7636)
38
+ - `AuthorizationResponse.parse`, which reads the response an authorization server returns to the redirect URI: it raises the error the server reported, a `state` that is not the one the request sent, an `iss` that is not the expected issuer (RFC 9207), a repeated parameter, or a response with no code
39
+ - A `params` option on every request builder, for what an extension adds to a request, such as the resource indicator of RFC 8707
40
+ - `Token.from_response` and `Error.from_response` for token and error responses, rejecting a response whose access token is missing, null, or empty, or whose lifetime is not a number of seconds
20
41
 
21
- ### Fixed
42
+ ### Changed
22
43
 
23
- * Treat only `&` as a parameter separator when reading a query string or form body; a `;` is part of the value, as every current server reads it, so `?a=1;b=2` no longer signs two parameters where the server sees one
44
+ - Every file declares `frozen_string_literal: true`, and the explicit `freeze` calls on string literals it makes redundant are gone
45
+ - Drop the `base64` runtime dependency: `pack("m0")` and `unpack1("m")` encode and decode Base64 in core Ruby, so the gem now has no runtime dependencies at all
46
+ - Drop the `cgi` runtime dependency: query strings and form bodies are read with `URI.decode_www_form`, which Ruby ships in every supported version
47
+ - **Breaking:** Raise `ParseError` when an Authorization header, form body, or query string repeats an OAuth protocol parameter, which RFC 5849 Section 3.2 does not allow; `Header.parse` previously kept the last value and `Header.parse_form_body` the first, so the two disagreed about which one a request carried
48
+ - **Breaking:** Rename `Signature.methods` to `Signature.registered_methods`, so that `Signature.methods` is the module's own method list again
49
+ - **Breaking:** Define `VERSION` in `SimpleOAuth`, the module the rest of the library uses, rather than in a second `SimpleOauth` module; `SimpleOauth::VERSION` is gone and `SimpleOAuth::VERSION` now resolves
50
+ - **Breaking:** `Signature.rsa?` raises `ArgumentError` for a signature method that is not registered, as `Signature.digest`, `sign`, and `verify` already do, rather than answering false
51
+ - `SimpleOAuth::Error` is the base of every error the library raises, so `rescue SimpleOAuth::Error` catches `ParseError`, `InvalidOptionsError`, and `OAuth2::Error` alike
24
52
 
25
- ### Added
53
+ ### Fixed
26
54
 
27
- * OAuth 2.0 request builders and response parsers in `SimpleOAuth::OAuth2`, which make no HTTP requests:
28
- * `Client#authorization_url` for the authorization code flow, which names `pkce` so that a client either sends a challenge or says `pkce: nil`, as OAuth 2.1 asks every client for one; a `state` is optional alongside a challenge, and required without one
29
- * `Client#authorization_code_request`, `#refresh_token_request`, and `#client_credentials_request` for the token endpoint
30
- * `Client#revocation_request` for the revocation endpoint (RFC 7009)
31
- * `client_secret_basic` and `client_secret_post` authentication for confidential clients, and public clients without a secret
32
- * `PKCE` verifiers with `S256` and `plain` challenges (RFC 7636)
33
- * `AuthorizationResponse.parse`, which reads the response an authorization server returns to the redirect URI: it raises the error the server reported, a `state` that is not the one the request sent, an `iss` that is not the expected issuer (RFC 9207), a repeated parameter, or a response with no code
34
- * A `params` option on every request builder, for what an extension adds to a request, such as the resource indicator of RFC 8707
35
- * `Token.from_response` and `Error.from_response` for token and error responses, rejecting a response whose access token is missing, null, or empty, or whose lifetime is not a number of seconds
55
+ - Treat only `&` as a parameter separator when reading a query string or form body; a `;` is part of the value, as every current server reads it, so `?a=1;b=2` no longer signs two parameters where the server sees one
36
56
 
37
57
  ## [0.5.1] - 2026-09-12
38
58
 
39
59
  ### Fixed
40
60
 
41
- * Build the signature base string with a String conversion that every supported Ruby offers; `Header#url` called `URI::Generic#to_str`, which arrived in `uri` 0.13, so it raised `NoMethodError` on a stock Ruby 3.2, the oldest version the gem claims to support
42
- * Sign a parameter that carries no value, such as a bare `?flag` in the query string or the `c2` of the RFC 5849 Section 3.4.1.3.1 example, as `name=` rather than dropping it from the signature base string; a request carrying one signed differently than the server computes, so it was rejected
61
+ - Build the signature base string with a String conversion that every supported Ruby offers; `Header#url` called `URI::Generic#to_str`, which arrived in `uri` 0.13, so it raised `NoMethodError` on a stock Ruby 3.2, the oldest version the gem claims to support
62
+ - Sign a parameter that carries no value, such as a bare `?flag` in the query string or the `c2` of the RFC 5849 Section 3.4.1.3.1 example, as `name=` rather than dropping it from the signature base string; a request carrying one signed differently than the server computes, so it was rejected
43
63
 
44
64
  ## [0.5.0] - 2026-09-12
45
65
 
46
66
  ### Added
47
67
 
48
- * `Header.from_request`, which builds a header for a request object such as a `Net::HTTPRequest`, signing its query parameters, its form-encoded body, or hashing any other body
49
- * `Header.parse_query`, for OAuth credentials sent in a query string
50
- * `Signature.digest`, and a `digest:` option on `Signature.register`, which gives the hash algorithm a signature method signs with
51
- * `Signature.verify` and a `verify:` option on `Signature.register`, for signature methods that cannot be verified by recomputing the signature
52
- * `Signature.decode_base64`
68
+ - `Header.from_request`, which builds a header for a request object such as a `Net::HTTPRequest`, signing its query parameters, its form-encoded body, or hashing any other body
69
+ - `Header.parse_query`, for OAuth credentials sent in a query string
70
+ - `Signature.digest`, and a `digest:` option on `Signature.register`, which gives the hash algorithm a signature method signs with
71
+ - `Signature.verify` and a `verify:` option on `Signature.register`, for signature methods that cannot be verified by recomputing the signature
72
+ - `Signature.decode_base64`
53
73
 
54
74
  ### Fixed
55
75
 
56
- * Check `oauth_body_hash` against the body a header was built with when verifying, so a body changed after signing no longer verifies against the hash its signature covers
57
- * Verify signatures without merging the given secrets into the header's own options, where anything else reading the header could see them
58
- * Compare signatures in constant time when verifying
59
- * Compute `oauth_body_hash` with the hash algorithm of the signature method, such as SHA-256 for HMAC-SHA256; it was always SHA-1
60
- * Sign a parameter whose value is an Array as one parameter per value, as a repeated parameter; the Array was previously signed as its Ruby representation
61
- * Verify RSA signatures with the signer's public key, which is all a verifier has; `Header#valid?` previously recomputed the signature and so needed the private key
62
- * Match the form-encoded media type exactly when signing a body, rather than by prefix, so a media type such as `application/x-www-form-urlencoded-json` is hashed instead of signed as parameters, and `Application/X-WWW-Form-Urlencoded` is recognized
76
+ - Check `oauth_body_hash` against the body a header was built with when verifying, so a body changed after signing no longer verifies against the hash its signature covers
77
+ - Verify signatures without merging the given secrets into the header's own options, where anything else reading the header could see them
78
+ - Compare signatures in constant time when verifying
79
+ - Compute `oauth_body_hash` with the hash algorithm of the signature method, such as SHA-256 for HMAC-SHA256; it was always SHA-1
80
+ - Sign a parameter whose value is an Array as one parameter per value, as a repeated parameter; the Array was previously signed as its Ruby representation
81
+ - Verify RSA signatures with the signer's public key, which is all a verifier has; `Header#valid?` previously recomputed the signature and so needed the private key
82
+ - Match the form-encoded media type exactly when signing a body, rather than by prefix, so a media type such as `application/x-www-form-urlencoded-json` is hashed instead of signed as parameters, and `Application/X-WWW-Form-Urlencoded` is recognized
63
83
 
64
84
  ## [0.4.2] - 2026-09-12
65
85
 
66
86
  ### Added
67
87
 
68
- * Document passing `Header` parameters as an Array of key-value pairs when a key repeats
88
+ - Document passing `Header` parameters as an Array of key-value pairs when a key repeats
69
89
 
70
90
  ### Fixed
71
91
 
72
- * Accept an Array of key-value pairs as `Header` parameters in the RBS signatures, which already worked at runtime
92
+ - Accept an Array of key-value pairs as `Header` parameters in the RBS signatures, which already worked at runtime
73
93
 
74
94
  ## [0.4.1] - 2026-04-20
75
95
 
76
96
  ### Fixed
77
97
 
78
- * Remove `URI::RFC2396_PARSER` stub from RBS signatures to avoid duplicate declaration error with RBS 4.0.2, which now ships the constant in its stdlib `uri` signatures
98
+ - Remove `URI::RFC2396_PARSER` stub from RBS signatures to avoid duplicate declaration error with RBS 4.0.2, which now ships the constant in its stdlib `uri` signatures
79
99
 
80
100
  ## [0.4.0] - 2026-02-01
81
101
 
82
102
  ### Added
83
103
 
84
- * Extensible signature method registry allowing custom signature methods to be registered at runtime
85
- * Support for RSA-SHA256 and HMAC-SHA256 signature methods
86
- * OAuth Request Body Hash support (`oauth_body_hash` parameter) for signing requests with non-form-encoded bodies
87
- * Support for parsing OAuth credentials from POST body via `Header.parse_form_body`
88
- * Support for `realm` parameter in OAuth Authorization header
89
-
90
- ### Fixed
91
-
92
- * Avoid symbolizing untrusted input in parse methods for security
93
- * Refactored `Header.parse` for improved robustness using StringScanner
104
+ - Extensible signature method registry allowing custom signature methods to be registered at runtime
105
+ - Support for RSA-SHA256 and HMAC-SHA256 signature methods
106
+ - OAuth Request Body Hash support (`oauth_body_hash` parameter) for signing requests with non-form-encoded bodies
107
+ - Support for parsing OAuth credentials from POST body via `Header.parse_form_body`
108
+ - Support for `realm` parameter in OAuth Authorization header
94
109
 
95
110
  ### Changed
96
111
 
97
- * **Breaking**: `Signature.rsa?` raises `ArgumentError` for a signature method that is not registered, as `Signature.digest`, `sign`, and `verify` already do, rather than answering false
98
- * `SimpleOAuth::Error` is the base of every error the library raises, so `rescue SimpleOAuth::Error` catches `ParseError`, `InvalidOptionsError`, and `OAuth2::Error` alike
112
+ - **Breaking:** Require Ruby 3.2 or later; supports Ruby 3.2, 3.3, 3.4, and 4.0
113
+ - Added `base64` and `cgi` as explicit runtime dependencies
114
+ - Migrated test suite from RSpec to Minitest
99
115
 
100
- * Supports Ruby 3.2, 3.3, 3.4, and 4.0
101
- * Added `base64` and `cgi` as explicit runtime dependencies
102
- * Migrated test suite from RSpec to Minitest
116
+ ### Fixed
103
117
 
118
+ - Avoid symbolizing untrusted input in parse methods for security
119
+ - Refactored `Header.parse` for improved robustness using StringScanner
120
+
121
+ [1.0.2]: https://github.com/laserlemon/simple_oauth/compare/v1.0.1...v1.0.2
122
+ [1.0.1]: https://github.com/laserlemon/simple_oauth/compare/v1.0.0...v1.0.1
123
+ [1.0.0]: https://github.com/laserlemon/simple_oauth/compare/v0.5.1...v1.0.0
124
+ [0.5.1]: https://github.com/laserlemon/simple_oauth/compare/v0.5.0...v0.5.1
125
+ [0.5.0]: https://github.com/laserlemon/simple_oauth/compare/v0.4.2...v0.5.0
126
+ [0.4.2]: https://github.com/laserlemon/simple_oauth/compare/v0.4.1...v0.4.2
127
+ [0.4.1]: https://github.com/laserlemon/simple_oauth/compare/v0.4.0...v0.4.1
128
+ [0.4.0]: https://github.com/laserlemon/simple_oauth/compare/v0.3.1...v0.4.0
data/README.md CHANGED
@@ -6,6 +6,7 @@
6
6
  [![Lint](https://github.com/laserlemon/simple_oauth/actions/workflows/lint.yml/badge.svg)](https://github.com/laserlemon/simple_oauth/actions/workflows/lint.yml)
7
7
  [![Typecheck](https://github.com/laserlemon/simple_oauth/actions/workflows/typecheck.yml/badge.svg)](https://github.com/laserlemon/simple_oauth/actions/workflows/typecheck.yml)
8
8
  [![Yardstick](https://github.com/laserlemon/simple_oauth/actions/workflows/yardstick.yml/badge.svg)](https://github.com/laserlemon/simple_oauth/actions/workflows/yardstick.yml)
9
+ [![Duel](https://raw.githubusercontent.com/laserlemon/simple_oauth/duel/badge/badge.svg)](https://github.com/sferik/duel-ruby)
9
10
 
10
11
  Simply builds and verifies OAuth 1.0 headers per [RFC 5849](https://tools.ietf.org/html/rfc5849), and builds OAuth 2.0 requests per [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749), [RFC 7636](https://www.rfc-editor.org/rfc/rfc7636), and [RFC 7009](https://www.rfc-editor.org/rfc/rfc7009).
11
12
 
@@ -158,16 +159,18 @@ parsed = SimpleOAuth::Header.parse_query("oauth_consumer_key=key&status=hello")
158
159
  header = SimpleOAuth::Header.new(:get, request_url, params, authorization_header)
159
160
 
160
161
  # Verify the signature
161
- header.valid?(consumer_secret: "secret", token_secret: "token_secret")
162
+ header.valid?(consumer_secret: "secret", token_secret: "token_secret", signature_method: "HMAC-SHA1")
162
163
  # => true
163
164
  ```
164
165
 
166
+ Name the signature method you expect, and a header claiming any other method is not valid. The method a request claims is chosen by whoever sent it, so verifying with it unchecked would let anyone holding an RSA public key pass it off as an HMAC or PLAINTEXT secret. Leaving `signature_method` out is deprecated: the header must then claim an RSA method for an RSA key and an HMAC method for any other secret. PLAINTEXT signatures are accepted only when you name `PLAINTEXT`, and they send the secrets themselves, so accept them only over TLS.
167
+
165
168
  Verifying compares signatures in constant time and leaves the header's own options untouched, so the secrets stay with the caller.
166
169
 
167
170
  RSA signatures verify with the client's public key, which is all a server has:
168
171
 
169
172
  ```ruby
170
- header.valid?(consumer_secret: File.read("client_public_key.pem"))
173
+ header.valid?(consumer_secret: File.read("client_public_key.pem"), signature_method: "RSA-SHA1")
171
174
  ```
172
175
 
173
176
  Custom signature methods that cannot be verified by recomputing the signature register a `verify` block:
data/Rakefile CHANGED
@@ -41,3 +41,10 @@ task :steep do
41
41
  end
42
42
 
43
43
  task default: %i[test rubocop standard mutant yardstick steep]
44
+
45
+ begin
46
+ require "duel/rake_task"
47
+ Duel::RakeTask.new
48
+ rescue LoadError
49
+ # Duel requires Ruby 3.4 or later
50
+ end
@@ -24,6 +24,9 @@ module SimpleOAuth
24
24
  # Default signature method per RFC 5849
25
25
  DEFAULT_SIGNATURE_METHOD = "HMAC-SHA1"
26
26
 
27
+ # The signature method that sends the secrets themselves, which verifying accepts only when named
28
+ PLAINTEXT_SIGNATURE_METHOD = "PLAINTEXT"
29
+
27
30
  # OAuth version
28
31
  OAUTH_VERSION = "1.0"
29
32
 
@@ -123,18 +126,30 @@ module SimpleOAuth
123
126
 
124
127
  # Validates the signature in the header against the provided secrets
125
128
  #
129
+ # The method a received header claims is chosen by whoever sent it, so it is checked before it
130
+ # is used. Given a signature_method, the header must claim that method. Without one, the header
131
+ # must claim a method that fits the key: an RSA method for an RSA key, and an HMAC method for any
132
+ # other secret. PLAINTEXT is accepted only when named, as it sends the secrets themselves.
133
+ # Without that check, a sender holding only an RSA public key could sign with it as an HMAC or
134
+ # PLAINTEXT secret.
135
+ #
126
136
  # @api public
127
- # @param secrets [Hash] the consumer_secret and token_secret for validation
137
+ # @param secrets [Hash] the consumer_secret and token_secret for validation, and the
138
+ # signature_method the header must use, which a later major version will require
128
139
  # @return [Boolean] true if the signature is valid, false otherwise
140
+ # @raise [ArgumentError] if the expected signature method is not registered
129
141
  # @note When the header was built with a body, the signed oauth_body_hash must match that body,
130
142
  # so a tampered body fails verification even though its signature covers the claimed hash
131
143
  # @example
132
144
  # parsed_header = SimpleOAuth::Header.new(:get, url, {}, authorization_header)
133
- # parsed_header.valid?(consumer_secret: "secret", token_secret: "token_secret")
145
+ # parsed_header.valid?(consumer_secret: "secret", token_secret: "token_secret",
146
+ # signature_method: "HMAC-SHA1")
134
147
  # # => true
135
148
  def valid?(secrets = {})
136
- body_hash_valid? && Signature.verify(options.fetch(:signature_method), signing_key(options.merge(secrets)),
137
- signature_base, options.fetch(:signature))
149
+ verifying = options.merge(secrets)
150
+ signature_method_acceptable?(verifying, secrets[:signature_method]) && body_hash_valid? &&
151
+ Signature.verify(options.fetch(:signature_method), signing_key(verifying), signature_base,
152
+ options.fetch(:signature))
138
153
  end
139
154
 
140
155
  # Returns the OAuth attributes including the signature
@@ -216,6 +231,50 @@ module SimpleOAuth
216
231
  Signature.rsa?(options.fetch(:signature_method)) ? options[:consumer_secret] : secret(options)
217
232
  end
218
233
 
234
+ # Checks that the header claims a signature method the verifier accepts
235
+ #
236
+ # @api private
237
+ # @param verifying [Hash] the header's options merged with the verifier's secrets
238
+ # @param expected [String, nil] the signature method the verifier expects, if it names one
239
+ # @return [Boolean] true if the claimed method is the expected one, or fits the key
240
+ # @raise [ArgumentError] if the expected signature method is not registered
241
+ def signature_method_acceptable?(verifying, expected)
242
+ claimed = options[:signature_method].to_s
243
+ return expected_signature_method?(claimed, expected) if expected
244
+
245
+ warn("SimpleOAuth::Header#valid? without a signature_method is deprecated. " \
246
+ "Name the method to verify with, such as signature_method: \"HMAC-SHA1\"", category: :deprecated)
247
+ Signature.registered?(claimed) && !PLAINTEXT_SIGNATURE_METHOD.casecmp?(claimed) &&
248
+ Signature.rsa?(claimed).eql?(rsa_key?(verifying[:consumer_secret]))
249
+ end
250
+
251
+ # Checks that the header claims the signature method the verifier names
252
+ #
253
+ # Signature method names are compared without regard to case, as the registry looks them up.
254
+ #
255
+ # @api private
256
+ # @param claimed [String] the signature method the header claims
257
+ # @param expected [String] the signature method the verifier expects
258
+ # @return [Boolean] true if the header claims the expected method
259
+ # @raise [ArgumentError] if the expected signature method is not registered
260
+ def expected_signature_method?(claimed, expected)
261
+ raise ArgumentError, "Unknown signature method: #{expected}" unless Signature.registered?(expected)
262
+
263
+ claimed.casecmp?(expected)
264
+ end
265
+
266
+ # Checks whether a verifying key is an RSA key
267
+ #
268
+ # @api private
269
+ # @param key [String, nil] the consumer_secret the verifier gave
270
+ # @return [Boolean] true if the key parses as an RSA key
271
+ def rsa_key?(key)
272
+ OpenSSL::PKey::RSA.new(key.to_s)
273
+ true
274
+ rescue OpenSSL::PKey::PKeyError
275
+ false
276
+ end
277
+
219
278
  # Checks the body against the oauth_body_hash the header carries
220
279
  #
221
280
  # A header parsed from a request claims a body hash that its signature covers, so the claim must be
@@ -3,5 +3,5 @@
3
3
  # OAuth 1.0 header generation library
4
4
  module SimpleOAuth
5
5
  # The current version of the SimpleOAuth gem
6
- VERSION = "1.0.1"
6
+ VERSION = "1.0.2"
7
7
  end
data/sig/simple_oauth.rbs CHANGED
@@ -62,6 +62,9 @@ module SimpleOAuth
62
62
  # Valid keys when parsing OAuth parameters (ATTRIBUTE_KEYS + signature)
63
63
  PARSE_KEYS: Array[Symbol]
64
64
 
65
+ # The signature method that sends the secrets themselves, which verifying accepts only when named
66
+ PLAINTEXT_SIGNATURE_METHOD: String
67
+
65
68
  # What from_request needs from a request object, such as a Net::HTTPRequest
66
69
  interface _Request
67
70
  def method: () -> String
@@ -148,6 +151,15 @@ module SimpleOAuth
148
151
  # The key for signing and verifying
149
152
  def signing_key: (oauth_options options) -> String?
150
153
 
154
+ # Checks that the header claims a signature method the verifier accepts
155
+ def signature_method_acceptable?: (oauth_options verifying, String? expected) -> bool
156
+
157
+ # Checks that the header claims the signature method the verifier names
158
+ def expected_signature_method?: (String claimed, String expected) -> bool
159
+
160
+ # Checks whether a verifying key is an RSA key
161
+ def rsa_key?: (String? key) -> bool
162
+
151
163
  # Checks the body against the oauth_body_hash the header carries
152
164
  def body_hash_valid?: () -> bool
153
165
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_oauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Richert
@@ -43,7 +43,6 @@ files:
43
43
  - lib/simple_oauth/parser.rb
44
44
  - lib/simple_oauth/signature.rb
45
45
  - lib/simple_oauth/version.rb
46
- - mutant.yml
47
46
  - sig/manifest.yaml
48
47
  - sig/simple_oauth.rbs
49
48
  - sig/simple_oauth/header/class_methods.rbs
@@ -74,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
74
73
  - !ruby/object:Gem::Version
75
74
  version: '0'
76
75
  requirements: []
77
- rubygems_version: 4.0.22
76
+ rubygems_version: 4.0.24
78
77
  specification_version: 4
79
78
  summary: Simply builds and verifies OAuth 1.0 headers and builds OAuth 2.0 requests
80
79
  test_files: []
data/mutant.yml DELETED
@@ -1,17 +0,0 @@
1
- usage: opensource
2
- integration:
3
- name: minitest
4
- includes:
5
- - lib
6
- - test
7
- requires:
8
- - simple_oauth
9
- mutation:
10
- operators: full
11
- timeout: 5.0
12
- matcher:
13
- subjects:
14
- - SimpleOAuth*
15
- coverage_criteria:
16
- process_abort: true
17
- test_result: true